[关闭]
@startover 2016-09-14T21:27:40.000000Z 字数 5176 阅读 1852

Python 程序员的 Golang 学习指南(III): 入门篇

Golang


基础语法

类型和关键字

  1. // 基础类型
  2. 布尔类型: bool
  3. 整型: int8uint8int16uint16int32uint32int64uint64intrunebytecomplex128 complex64,其中,byte int8 的别名
  4. 浮点类型: float32 float64
  5. 复数类型: complex64 complex128
  6. 字符串: string
  7. 字符类型: runeint32的别名)
  8. 错误类型: error
  9. // 复合类型
  10. 指针(pointer
  11. 数组(array
  12. 切片(slice
  13. 字典(map
  14. 通道(chan
  15. 结构体(struct
  16. 接口(interface
  1. break default func interface select
  2. case defer go map struct
  3. chan else goto package switch
  4. const fallthrough if range type
  5. continue for import return var

变量

Go 同其他语言不同的地方在于变量的类型在变量名的后面,不是 int a,而是 a int。至于为什么这么定义,Go 的官方博客有给出解释,有兴趣的可以参考下。

变量定义语法如下:

  1. var a int
  2. a = 2
  3. // 或者
  4. a := 2
  5. // 同时定义多个变量
  6. var (
  7. a int
  8. b bool
  9. )
  10. // 同时给多个变量赋值
  11. a, b := 2, true

操作符

  1. + & += &= && == != ( )
  2. - | -= |= || < <= [ ]
  3. * ^ *= ^= <- > >= { }
  4. / << /= <<= ++ = := , ;
  5. % >> %= >>= -- ! ... . :
  6. &^ &^=

控制结构

Go 语言支持如下的几种流程控制语句:

1. 条件语句,对应的关键字为 if、else 和 else if;

2. 选择语句,对应的关键字为 switch、case 和 select;

3. 循环语句,对应的关键字为 for 和 range;

4. 跳转语句,对应的关键字为 goto。

值得一提的是,Go 语言并不支持 do 或者 while 关键字,而是对 for 关键字做了增强,以实现类似的效果,如下:

  1. for {
  2. // 实现无限循环,慎用!
  3. }

常用内置函数

  1. len:计算(字符串,数组或者切片,map)长度
  2. cap:计算(数组或者切片,map)容量
  3. close:关闭通道
  4. append:追加内容到切片
  5. copy:拷贝数组/切片内容到另一个数组/切片
  6. delete:用于删除 map 的元素

array, slice 和 map

  1. // array
  2. a := [3]int{ 1, 2, 3 } // 等价于 a := [...]int{ 1, 2, 3 }
  3. // slice
  4. s := make([]int , 3) // 创建一个长度为 3 的 slice
  5. s := append(s, 1) // 向 slice 追加元素
  6. s := append(s, 2)
  7. // map
  8. m := make(map[string]int) // 使用前必须先初始化
  9. m["golang"] = 7

关于 array, slice 和 map 的更多惯用法,有一篇文章介绍的挺详细,有兴趣的可以看看。

函数

Go 语言的函数有如下特性:

由于 Go 语言不支持函数重载(具体原因见 Go Language FAQ),但我们可以通过不定参数实现类似的效果。

  1. func myfunc(args ...int) {
  2. // TODO
  3. }
  4. // 可通过如下方式调用
  5. myfunc(2)
  6. myfunc(1, 3, 5)

与 C、C++ 和 Java 等开发语言的一个极大不同在于,Go 语言的函数或者成员的方法可以有多
个返回值,这个特性能够使我们写出比其他语言更优雅、更简洁的代码。

  1. func (file *File) Read(b []byte) (n int, err error)
  2. // 我们可以通过下划线(_)来忽略某个返回值
  3. n, _ := f.Read(buf)

匿名函数是指不需要定义函数名的一种函数实现方式,它并不是一个新概念,最早可以回溯
到 1958 年的 Lisp 语言。但是由于各种原因,C 和 C++ 一直都没有对匿名函数给以支持,其他的各
种语言,比如 JavaScript、C# 和 Objective-C 等语言都提供了匿名函数特性,当然也包含Go语言。

匿名函数由一个不带函数名的函数声明和函数体组成,如下:

  1. func(a, b int) bool {
  2. return a < b
  3. }

匿名函数可以直接赋值给一个变量或者直接执行:

  1. f := func(a, b int) bool {
  2. return a < b
  3. }
  4. func(a, b int) bool {
  5. return a < b
  6. }(3, 4) // 花括号后直接跟参数列表表示函数调用

闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者
任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含
在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环
境(作用域)。

Go 的匿名函数就是一个闭包。我们来看一个例子:

  1. package main
  2. import "fmt"
  3. func main() {
  4. j := 5
  5. a := func() func() {
  6. i := 10
  7. return func() {
  8. fmt.Printf("i, j: %d, %d\n", i, j)
  9. }
  10. }()
  11. a()
  12. j *= 2
  13. a()
  14. }

程序输出如下:

  1. i, j: 10, 5
  2. i, j: 10, 10

错误处理

Go 语言追求简洁优雅,所以,Go 语言不支持传统的 try...catch...finally 这种异常,因为 Go 语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在 Go 语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,也就是说,遇到真正的异常的情况下(比如除数为0了),才使用 Go 中引入的Exception处理:defer, panic, recover。

用法如下:

  1. package main
  2. import "fmt"
  3. func main() {
  4. defer func() {
  5. fmt.Println("recovered:", recover())
  6. }()
  7. panic("not good")
  8. }

关于 Go 语言的错误处理机制和传统的 try...catch...finally 异常机制孰优孰劣,属于仁者见仁,智者见智,这里不做赘速。有兴趣的同学可以去看看知乎上的讨论:Go 语言的错误处理机制是一个优秀的设计吗?

面向对象 -> 一切皆类型

Python 推崇“一切皆对象”,而在 Go 语言中,类型才是一等公民。

我们可以这样定义一个结构体:

  1. type Name struct {
  2. First string
  3. Middle string
  4. Last string
  5. }

同样也可以定义基础类型:

  1. type SimpleName string

还能给任意类型定义方法:

  1. func (s SimpleName) String() string { return string(s) }
  2. // 或者
  3. func (s string) NoWay()

Golang VS Python

最后我们通过几个例子来比较一下 Golang 与 Python 的一些基本用法,如下:

生成器(Generator)

  1. def fib(n):
  2. a, b = 0, 1
  3. for i in range(n):
  4. a, b = b, a + b
  5. yield a
  6. for x in fib(10):
  7. print x
  8. print 'done'
  1. package main
  2. import "fmt"
  3. func fib(n int) chan int {
  4. c := make(chan int)
  5. go func() {
  6. a, b := 0, 1
  7. for i := 0; i < n; i++ {
  8. a, b = b, a+b
  9. c <- a
  10. }
  11. close(c)
  12. }()
  13. return c
  14. }
  15. func main() {
  16. for x := range fib(10) {
  17. fmt.Println(x)
  18. }
  19. }

装饰器(Decorator)

  1. from urlparse import urlparse, parse_qs
  2. from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
  3. def auth_required(myfunc):
  4. def checkuser(self):
  5. user = parse_qs(urlparse(self.path).query).get('user')
  6. if user:
  7. self.user = user[0]
  8. myfunc(self)
  9. else:
  10. self.wfile.write('unknown user')
  11. return checkuser
  12. class myHandler(BaseHTTPRequestHandler):
  13. @auth_required
  14. def do_GET(self):
  15. self.wfile.write('Hello, %s!' % self.user)
  16. if __name__ == '__main__':
  17. try:
  18. server = HTTPServer(('localhost', 8080), myHandler)
  19. server.serve_forever()
  20. except KeyboardInterrupt:
  21. server.socket.close()
  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. var hiHandler = authRequired(
  7. func(w http.ResponseWriter, r *http.Request) {
  8. fmt.Fprintf(w, "Hi, %v", r.FormValue("user"))
  9. },
  10. )
  11. func authRequired(f http.HandlerFunc) http.HandlerFunc {
  12. return func(w http.ResponseWriter, r *http.Request) {
  13. if r.FormValue("user") == "" {
  14. http.Error(w, "unknown user", http.StatusForbidden)
  15. return
  16. }
  17. f(w, r)
  18. }
  19. }
  20. func main() {
  21. http.HandleFunc("/hi", hiHandler)
  22. http.ListenAndServe(":8080", nil)
  23. }

猴子补丁(Monkey patching)

  1. import urllib
  2. def say_hi(usr):
  3. if auth(usr):
  4. print 'Hi, %s' % usr
  5. else:
  6. print 'unknown user %s' % usr
  7. def auth(usr):
  8. try:
  9. auth_url = 'localhost'
  10. r = urllib.urlopen(auth_url + '/' + usr)
  11. return r.getcode() == 200
  12. except:
  13. return False
  14. def sayhitest():
  15. # Test authenticated user
  16. globals()['auth'] = lambda x: True
  17. say_hi('John')
  18. # Test unauthenticated user
  19. globals()['auth'] = lambda x: False
  20. say_hi('John')
  21. if __name__ == '__main__':
  22. sayhitest()
  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. func sayHi(user string) {
  7. if !auth(user) {
  8. fmt.Printf("unknown user %v\n", user)
  9. return
  10. }
  11. fmt.Printf("Hi, %v\n", user)
  12. }
  13. var auth = func(user string) bool {
  14. authURL := "localhost"
  15. res, err := http.Get(authURL + "/" + user)
  16. return err == nil && res.StatusCode == http.StatusOK
  17. }
  18. func testSayHi() {
  19. auth = func(string) bool { return true }
  20. sayHi("John")
  21. auth = func(string) bool { return false }
  22. sayHi("John")
  23. }
  24. func main() {
  25. testSayHi()
  26. }

相关链接:
https://blog.golang.org/gos-declaration-syntax
https://se77en.cc/2014/06/30/array-slice-map-and-set-in-golang/
https://golang.org/doc/faq#overloading
https://www.zhihu.com/question/27158146
https://talks.golang.org/2013/go4python.slide

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注