Python 程式設計師的 Golang 學習指南(III): 入門篇
Authors: startover
基礎語法
型別和關鍵字
- 型別
// 基礎型別
布林型別: bool
整型: int8,uint8,int16,uint16,int32,uint32,int64,uint64,int,rune,byte,complex128, complex64,其中,byte 是 int8 的別名
浮點型別: float32 、 float64
複數型別: complex64 、 complex128
字串: string
字元型別: rune(int32的別名)
錯誤型別: error
// 複合型別
指標(pointer)
陣列(array)
切片(slice)
字典(map)
通道(chan)
結構體(struct)
介面(interface)
- 關鍵字
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
變數
Go 同其他語言不同的地方在於變數的型別在變數名的後面,不是 int a
,而是 a int
。至於為什麼這麼定義,Go 的官方部落格有給出解釋,有興趣的可以參考下。
變數定義語法如下:
var a int
a = 2
// 或者
a := 2
// 同時定義多個變數
var (
a int
b bool
)
// 同時給多個變數賦值
a, b := 2, true
操作符
+ & += &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^=
控制結構
Go 語言支援如下的幾種流程控制語句:
- 條件語句,對應的關鍵字為 if、else 和 else if;
- 選擇語句,對應的關鍵字為 switch、case 和 select;
- 迴圈語句,對應的關鍵字為 for 和 range;
- 跳轉語句,對應的關鍵字為 goto。
值得一提的是,Go 語言並不支援 do 或者 while 關鍵字,而是對 for 關鍵字做了增強,以實現類似的效果,如下:
for {
// 實現無限迴圈,慎用!
}
常用內建函式
- len:計算(字串,陣列或者切片,map)長度
- cap:計算(陣列或者切片,map)容量
- close:關閉通道
- append:追加內容到切片
- copy:拷貝陣列/切片內容到另一個陣列/切片
- delete:用於刪除 map 的元素
array, slice 和 map
// array
a := [3]int{ 1, 2, 3 } // 等價於 a := [...]int{ 1, 2, 3 }
// slice
s := make([]int , 3) // 建立一個長度為 3 的 slice
s := append(s, 1) // 向 slice 追加元素
s := append(s, 2)
// map
m := make(map[string]int) // 使用前必須先初始化
m["golang"] = 7
關於 array, slice 和 map 的更多慣用法,有一篇文章介紹的挺詳細,有興趣的可以看看。
函式
Go 語言的函式有如下特性:
- 不定引數
由於 Go 語言不支援函式過載(具體原因見 Go Language FAQ),但我們可以通過不定引數實現類似的效果。
func myfunc(args ...int) {
// TODO
}
// 可通過如下方式呼叫
myfunc(2)
myfunc(1, 3, 5)
- 多返回值
與 C、C++ 和 Java 等開發語言的一個極大不同在於,Go 語言的函式或者成員的方法可以有多 個返回值,這個特效能夠使我們寫出比其他語言更優雅、更簡潔的程式碼。
func (file *File) Read(b []byte) (n int, err error)
// 我們可以通過下劃線(_)來忽略某個返回值
n, _ := f.Read(buf)
- 匿名函式
匿名函式是指不需要定義函式名的一種函式實現方式,它並不是一個新概念,最早可以回溯 到 1958 年的 Lisp 語言。但是由於各種原因,C 和 C++ 一直都沒有對匿名函式給以支援,其他的各 種語言,比如 JavaScript、C# 和 Objective-C 等語言都提供了匿名函式特性,當然也包含 Go 語言。
匿名函式由一個不帶函式名的函式宣告和函式體組成,如下:
func(a, b int) bool {
return a < b
}
匿名函式可以直接賦值給一個變數或者直接執行:
f := func(a, b int) bool {
return a < b
}
func(a, b int) bool {
return a < b
}(3, 4) // 花括號後直接跟引數列表表示函式呼叫
- 閉包
閉包是可以包含自由 (未繫結到特定物件) 變數的程式碼塊,這些變數不在這個程式碼塊內或者 任何全域性上下文中定義,而是在定義程式碼塊的環境中定義。要執行的程式碼塊 (由於自由變數包含 在程式碼塊中,所以這些自由變數以及它們引用的物件沒有被釋放) 為自由變數提供繫結的計算環 境 (作用域)。
Go 的匿名函式就是一個閉包。我們來看一個例子:
package main
import "fmt"
func main() {
j := 5
a := func() func() {
i := 10
return func() {
fmt.Printf("i, j: %d, %d\n", i, j)
}
}()
a()
j *= 2
a()
}
程式輸出如下:
i, j: 10, 5
i, j: 10, 10
錯誤處理
Go 語言追求簡潔優雅,所以,Go 語言不支援傳統的 try...catch...finally
這種異常,因為 Go 語言的設計者們認為,將異常與控制結構混在一起會很容易使得程式碼變得混亂。因為開發者很容易濫用異常,甚至一個小小的錯誤都丟擲一個異常。在 Go 語言中,使用多值返回來返回錯誤。不要用異常代替錯誤,更不要用來控制流程。在極個別的情況下,也就是說,遇到真正的異常的情況下(比如除數為 0 了),才使用 Go 中引入的 Exception 處理:defer, panic, recover。
用法如下:
package main
import "fmt"
func main() {
defer func() {
fmt.Println("recovered:", recover())
}()
panic("not good")
}
關於 Go 語言的錯誤處理機制和傳統的 try...catch...finally
異常機制孰優孰劣,屬於仁者見仁,智者見智,這裡不做贅速。有興趣的同學可以去看看知乎上的討論:Go 語言的錯誤處理機制是一個優秀的設計嗎?。
物件導向 -> 一切皆型別
Python 推崇 “一切皆物件”,而在 Go 語言中,型別才是一等公民。
我們可以這樣定義一個結構體:
type Name struct {
First string
Middle string
Last string
}
同樣也可以定義基礎型別:
type SimpleName string
還能給任意型別定義方法:
func (s SimpleName) String() string { return string(s) }
// 或者
func (s string) NoWay()
Golang VS Python
最後我們通過幾個例子來比較一下 Golang 與 Python 的一些基本用法,如下:
生成器(Generator)
- Python 版本
def fib(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
yield a
for x in fib(10):
print x
print 'done'
- Golang 版本
package main
import "fmt"
func fib(n int) chan int {
c := make(chan int)
go func() {
a, b := 0, 1
for i := 0; i < n; i++ {
a, b = b, a+b
c <- a
}
close(c)
}()
return c
}
func main() {
for x := range fib(10) {
fmt.Println(x)
}
}
裝飾器(Decorator)
- Python 版本
from urlparse import urlparse, parse_qs
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
def auth_required(myfunc):
def checkuser(self):
user = parse_qs(urlparse(self.path).query).get('user')
if user:
self.user = user[0]
myfunc(self)
else:
self.wfile.write('unknown user')
return checkuser
class myHandler(BaseHTTPRequestHandler):
@auth_required
def do_GET(self):
self.wfile.write('Hello, %s!' % self.user)
if __name__ == '__main__':
try:
server = HTTPServer(('localhost', 8080), myHandler)
server.serve_forever()
except KeyboardInterrupt:
server.socket.close()
- Golang 版本
package main
import (
"fmt"
"net/http"
)
var hiHandler = authRequired(
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi, %v", r.FormValue("user"))
},
)
func authRequired(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.FormValue("user") == "" {
http.Error(w, "unknown user", http.StatusForbidden)
return
}
f(w, r)
}
}
func main() {
http.HandleFunc("/hi", hiHandler)
http.ListenAndServe(":8080", nil)
}
猴子補丁(Monkey patching)
- Python 版本
import urllib
def say_hi(usr):
if auth(usr):
print 'Hi, %s' % usr
else:
print 'unknown user %s' % usr
def auth(usr):
try:
auth_url = 'localhost'
r = urllib.urlopen(auth_url + '/' + usr)
return r.getcode() == 200
except:
return False
def sayhitest():
# Test authenticated user
globals()['auth'] = lambda x: True
say_hi('John')
# Test unauthenticated user
globals()['auth'] = lambda x: False
say_hi('John')
if __name__ == '__main__':
sayhitest()
- Golang 版本
package main
import (
"fmt"
"net/http"
)
func sayHi(user string) {
if !auth(user) {
fmt.Printf("unknown user %v\n", user)
return
}
fmt.Printf("Hi, %v\n", user)
}
var auth = func(user string) bool {
authURL := "localhost"
res, err := http.Get(authURL + "/" + user)
return err == nil && res.StatusCode == http.StatusOK
}
func testSayHi() {
auth = func(string) bool { return true }
sayHi("John")
auth = func(string) bool { return false }
sayHi("John")
}
func main() {
testSayHi()
}
相關連結:
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
本文章為 Cloudinsight 技術團隊工程師原創,更多技術文章可訪問 Cloudinsight 技術部落格。Cloudinsight 為視覺化系統監控工具,涵蓋 Windows、Linux 作業系統,用 Golang 開發的 Cloudinsight Agent 正式開源了,歡迎 fork,Github:https://github.com/cloudinsight/cloudinsight-agent
golang-for-pythonistas 系列持續更新中,歡迎關注,及時獲取最新文章~
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- Python 程式設計師的 Golang 學習指南(IV): 包管理篇Python程式設計師Golang
- Python 程式設計師的 Golang 學習指南 (I): Go 之初體驗Python程式設計師Golang
- Python 程式設計師的 Golang 學習指南(I): Go 之初體驗Python程式設計師Golang
- Python 程式設計師的 Golang 學習指南(II): 開發環境搭建Python程式設計師Golang開發環境
- Java工程師學習指南(入門篇)Java工程師
- Java工程師學習指南 入門篇Java工程師
- 好程式設計師Python學習路線之python爬蟲入門程式設計師Python爬蟲
- Android程式猿的react學習之路-入門指南篇AndroidReact
- 程式設計師英語學習指南程式設計師
- Let’s do this!新手程式設計師的入門指南程式設計師
- Python入門_給小白的學習指南Python
- 好程式設計師分享Python從入門到精通最佳學習路線程式設計師Python
- 寫給前端程式設計師的英文學習指南前端程式設計師
- 程式設計師生存指南讀書筆記-第三篇(學習)程式設計師筆記
- 遊戲程式設計入門指南遊戲程式設計
- shell程式設計入門指南程式設計
- Python入門深度學習完整指南Python深度學習
- 程式設計師分享前端初學者入門學習順序程式設計師前端
- 入門程式設計學習,適合學python語言嗎?程式設計Python
- 超詳細的程式設計師Java學習路線指南 ,從入門到精通 不看後悔程式設計師Java
- 程式設計師入門選擇書籍學習的利與弊程式設計師
- 入門級的程式設計師程式設計師
- 程式設計師程式設計入門一定知道!程式設計師需要學什麼?程式設計師
- 好程式設計師Java學習路線分享Redis快速入門程式設計師JavaRedis
- 程式設計師入門,7個方法幫助你提高學習效率!程式設計師
- 《Python遊戲程式設計入門》7.4習題Python遊戲程式設計
- 專為程式設計師編寫的英語學習指南程式設計師
- 程式設計師科學熬夜指南程式設計師
- Python程式設計入門Python程式設計
- 程式設計“初學者”入門指南 (轉)程式設計
- python程式設計真的好學嗎?python入門Python程式設計
- Python程式設計師學習路線圖Python程式設計師
- python程式設計:從入門到實踐學習筆記-字典Python程式設計筆記
- 《python 程式設計從入門到實踐》序:學習目標Python程式設計
- Python機器學習、深度學習:快速、完全的Numpy入門指南Python機器學習深度學習
- 設計小科普!給設計師的模組化設計新手完全入門指南
- 程式設計師健康防猝指南5:運動基礎入門程式設計師
- 程式設計師有哪些發展方向?linux運維入門學習程式設計師Linux運維