對比學習:Golang VS Python3
Golang和Python都是目前在各自領域最流行的開發語言之一。
Golang其高效而又友好的語法,贏得了很多後端開發人員的青睞,最適用於高併發網路程式設計的語言之一。
Python不用說,TIOBE排行榜的前十常駐居民,現在已經穩定在前五了。在機器學習、AI、資料分析領域成為必學語言。
兩門程式語言在語法上都有各自的特點,而且都易學易用。
本文對比這兩門語言目的不是爭誰優誰略,只是為了對比學習,適合掌握Python想學Go或者掌握Go想學Python的同學們參考。
Go和Python,一個是靜態語言一個是動態語言,從各個方面來看,都有根本性的差異,所以,文中很多內容不進行深入的比較了,我們只從程式設計師最直觀的語法面做對比。
為了便於閱讀,文中涉及程式碼都採用儘量簡單的語句呈現
字元編碼
Python
Python中預設的編碼格式是 ASCII 格式,程式檔案中如果包含中文字元(包括註釋部分)需要在檔案開頭加上 # -*- coding: UTF-8 -*- 或者 #coding=utf-8 就行了
Golang
原生支援Unicode
保留字(關鍵字)
Python
30個關鍵字
and exec not assert finally or break for pass class from print continue global raise def if return del import try elif in while else is with except lambda yield
Golang
25個關鍵字
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
註釋
Python
# 單行註釋 ''' 多行註釋 多行註釋 ''' """ 多行註釋 多行註釋 """
Golang
//單行註釋 /* 多行註釋 多行註釋 */
變數賦值
Python
Python是動態語言,所以在定義變數的時候不需要申明型別,直接使用即可。
Python會根據值判斷型別。
name = "Zeta" # 字串變數 age = 38 # 整數 income = 1.23 # 浮點數
多變數賦值
a,b = 1,2 # a=1; b=2 c = d = 3 # c=3; d=3
Golang
Go是靜態語言,是強型別的,但是Go語言也允許在賦值變數時確定型別。
因此Go有多種申明變數的方式
// 1. 完整的申明並賦值 var a int a = 1 // 2. 宣告變數型別同時賦值 var a int = 1 // 3. 不宣告型別,賦值時確定 var a = 1 // 4. 不用 var 關鍵字申明變數並賦值後確定型別 a := 1
注意,Go中的new關鍵字並不是宣告變數,而是返回該型別的指標
a := new(int) //這時候a是一個*int指標變數
標準資料型別
Python 的標準資料型別有:
- Boolean(布林值)
- Number(數字)
- String(字串)
- List(列表)
- Tuple(元組)
- Set(集合)
- Dictionary(字典)
Golang
- boolean(布林值)
- numeric(數字)
- string(字串)
- 陣列(陣列)
- slice(切片:不定長陣列)
- map(字典)
- struct(結構體)
- pointer(指標)
- function(函式)
- interface(介面)
- channel(通道)
總結
Python中的List列表對應Go語言中的Slice切片
Python中的Dictionary字典對應Go語言中的map
有一些值得注意的地方:
- Go是支援函式程式設計的語言,所以在Go語言中函式是一個型別
- Go語言不是物件導向的語言,沒有定義類的關鍵字Class,要實現OOP風格程式設計,是通過struct、interface型別實現的
- Python中的元組和集合在Go中都沒有
- channel是Go裡獨有的型別,多執行緒之間的通訊就靠它
資料型別轉換
Python
Python型別轉換非常簡單,用型別名作為函式名即可。
int(n) # 將數字n轉換為一個整數 float(n) # 將數字n轉換到一個浮點數 str(o) # 將物件 obj 轉換為字串 tuple(s) # 將序列 s 轉換為一個元組 list(s) # 將序列 s 轉換為一個列表 set(s) # 將序列 s 轉換為一個集合
Golang
Go語言的基礎型別轉換和Python差不多,也是用型別名作為函式名
i := 1024 f := float32(i) i = float32(f)
另外,Python中可以直接轉換數字字串和數字:
s = "123" i = 456 print(int(s), str(i))
但是Go是不可以的。
Go語言的字串處理很不同,string()只能用於[]byte型別轉換成字串,其他基礎型別的轉換需要用strconv包,另外,其他型別轉換成為string型別除了用strconv包,還可以用fmt.Sprintf函式:
package main import ( "fmt" "strconv" ) func main() { s := "123" i, _ := strconv.Atoi(s) println(i) s2 := fmt.Sprintf("%d", 456) println(s2) }
Go中的interface型別是不能直接轉換成其他型別的,需要使用到斷言
package main func main() { var itf interface{} = 1 i, ok := itf.(string) println("值:", i, "; 斷言結果", ok) j, ok := itf.(int) println("值:", j, "; 斷言結果", ok) }
輸出為:
值: ; 斷言結果 false 值: 1 ; 斷言結果 true
條件語句
Python
Python傳統的判斷語句如下
if name == 'zeta': # 判斷變數是否為 zeta print('Welcome boss') # 並輸出歡迎資訊 else: print('Hi, ' + name)
Python不支援三元表示式,但是可以用一種類似的替代辦法
title = "boss" name = "zeta" if title == "boss" else "chow" print(name)
邏輯與用 and ,邏輯或用 or
Golang
Go的if的語法類似Java,但是表示式不需要使用()
if a > b{ println("a > b") } else { println("a <= b") }
Go同樣沒有三元表示式,並且也沒有什麼替代方法。
另外,Go允許在if的表示式裡定義變數,定義並賦值的表示式與判斷的表示式用;隔開,常見的情況是獲取函式返回error,然後判斷error是否為空:
if err := foo(); err != nil { println("發生一些錯誤") }
與Python不同,邏輯與用 &&, 邏輯或用||
迴圈語句
Python
Python中有while和for兩種迴圈,都可以使用break跳出迴圈和continue立即進入下一輪迴圈,另外,Python的迴圈語句還可以用else執行迴圈全部完畢後的程式碼,break跳出後不會執行else的程式碼
while 條件迴圈,
count = 0 while (count < 9): print('The count is:', count) count = count + 1 if count == 5: break # 可以比較以下break和不break的區別 pass else: print('loop over')
for 遍歷迴圈,迴圈遍歷所有序列物件的子項
names = ['zeta', 'chow', 'world'] for n in names: print('Hello, ' + n) if n == 'world': break pass else: print('Good night!')
for迴圈中也可以用else,(註釋掉程式碼中的break試試看。)
Golang
Go語言只有一個迴圈語句for,但是根據不同的表示式,for有不同的表現
for 前置表示式; 條件表示式; 後置表示式 { //... }
前置表示式 在每輪迴圈前執行,可以用於宣告變數或呼叫函式返回;
條件表示式 滿足該表示式則執行下一輪迴圈,否則退出迴圈;
後置表示式 在迴圈完成後執行
經典的用法:
for i := 0; i < 10; i++ { println(i) }
我們可以忽略掉前置和後置表示式
sum := 1 for sum < 10 { sum += sum }
設定可以忽略掉全部表示式,也就是無限迴圈
for { print(".") }
Go的for迴圈同樣可以使用 break退出迴圈和continue立即進行下一輪迴圈。
for除了配合表示式迴圈,同樣也可以用於遍歷迴圈,需要用到range關鍵字
names := []string{"zeta", "chow", "world"} for i, n := range names { println(i,"Hello, " + n) }
函式
Python
用def關鍵字定義函式,並且在Python中,作為指令碼語言,呼叫函式必須在定義函式之後。
def foo(name): print("hello, "+name) pass foo("zeta")
預設引數 Python定義函式引數時,可以設定預設值,呼叫時如果沒有傳遞該引數,函式內將使用預設值,預設值引數必須放在無預設值引數後面。
def foo(name="zeta"): print("hello, "+name) pass foo()
關鍵字引數 一般函式傳遞引數時,必須按照引數定於的順序傳遞,但是Python中,允許使用關鍵字引數,這樣通過指定引數明,可以不按照函式定義引數的順序傳遞引數。
def foo(age, name="zeta"): print("hello, "+name+"; age="+str(age)) pass foo(name="chow", age=18)
不定長引數,Python支援不定長引數,用*定義引數名,呼叫時多個引數將作為一個元祖傳遞到函式內
def foo(*names): for n in names: print("hello, "+n) pass foo("zeta", "chow", "world")
return 返回函式結果。
Golang
Go用func定義函式,沒有預設值引數、沒有關鍵字引數,但是有很多其他特徵。
func main() { println(foo(18, "zeta")) } func foo(age int, name string) (r string) { r = fmt.Sprintf("myname is %s , age %d", name, age) return }
函式的定義和呼叫沒有順序的限制。
Go的函式不僅可以定義函式返回值型別,還可以申明返回值變數,當定義了返回值變數時,函式內的return語句可以不需要帶返回值,函式會預設使用返回值變數返回。
可變引數
使用…型別定義可變引數,函式內獲得的引數實際是該型別的slice物件
func main() { println(foo(18, “zeta”, “chow”, “world”)) } func foo(age int, names …string) (r string) { for _, n := range names { r += fmt.Sprintf(“myname is %s , age %d \n”, n, age) } return }
defer句
defer語句後面指定一個函式,該函式會延遲到本函式return後再執行。
defer語句在Go語言中非常有用,詳細可以查閱本專欄的另一篇文章《Golang研學:如何掌握並用好defer(延遲執行)》
func foo() { defer fmt.Println("defer run") fmt.Println("Hello world") return }
執行結果:
Hello world defer run
另外,在Go語言中函式也是型別,可以作為引數傳遞給別的函式
func main() { n := foo(func(i int, j int) int { return i + j }) println(n) } func foo(af func(int, int) int) int { return af(1, 2) }
上面這個例子直接在引數定義時使用函式型別,看上去有點混亂
再看來看一個清晰並完整的例子,說明全在註釋裡。
package main type math func(int, int) int //定義一個函式型別,兩個int引數,一個int返回值 //定義一個函式add,這個函式兩個int引數一個int返回值,與math型別相符 func add(i int, j int) int { return i + j } //再定義一個multiply,這個函式同樣符合math型別 func multiply(i, j int) int { return i * j } //foo函式,需要一個math型別的引數,用math型別的函式計算第2和第3個引數數字,並返回計算結果 //稍後在main中我們將add函式和multiply分別作為引數傳遞給它 func foo(m math, n1, n2 int) int { return m(1, 2) } func main() { //傳遞add函式和兩個數字,計算相加結果 n := foo(add, 1, 2) println(n) //傳遞multply和兩個數字,計算相乘結果 n = foo(multiply, 1, 2) println(n) }
結果
3 2
模組
Python
- 模組是一個.py檔案
- 模組在第一次被匯入時執行
- 一個下劃線定義保護級變數和函式,兩個下劃線定義私有變數和函式
- 匯入模組習慣性在指令碼頂部,但是不強制
Golang
- 與檔案和檔名無關,每一個檔案第一行用package定義包名,相同包名為一個包
- 包中的變數第一次引用時初始化,如果包中包含init函式,也會在第一次引用時執行(變數初始化後)
- 保重首寫字母大寫的函式和變數為共有,小寫字母為私有,Golang不是物件導向的,所以不存在保護級。
- 匯入模組必須寫在package之後,其他程式碼之前。
匯入包
Python
在Python中,使用import匯入模組。
#!/usr/bin/python # -*- coding: UTF-8 -*- # 匯入模組 import support support.print_func(“Runoob”)
還可以使用from import匯入模組指定部分
from modname import name1[, name2[, ... nameN]]
為匯入的包設定別名用 as關鍵字
import datetime as dt
Golang
也是使用import匯入包,匯入包指定的是包的路徑,包名預設為路徑中的最後部分
import "net/url" //匯入url包
多個包可以用()組合匯入
import ( "fmt" "net/url" )
為匯入的包設定別名, 直接在匯入包時,直接在報名前面新增別名,用空格隔開
import ( f "fmt" u "net/url" )
錯誤和異常
Python
Python中用經典的 try/except 捕獲異常
try: <語句> #執行別的程式碼 except <異常名稱>: <語句> # except <異常名稱>,<資料>: <語句> #如果引發了指定名稱的異常,獲得附加的資料
還提供了 else 和 finally
如果沒發生異常的執行else語句塊,finally塊的程式碼無論是否捕獲異常都會執行
Python內建了很全面的異常型別名稱,同時能自定義異常型別
Golang
Golang裡沒有用經典的 try/except捕獲異常。
Golang提供兩種錯誤處理方式
- 函式返回error型別物件判斷錯誤
- panic異常
一般情況下在Go裡只使用error型別判斷錯誤,Go官方希望開發者能夠很清楚的掌控所有的異常,在每一個可能出現異常的地方都返回或判斷error是否存在。
error是一個內建的介面型別
type error interface { Error() string }
通常,使用error異常處理類似這樣:
package main import "fmt" func foo(i int, j int) (r int, err error) { if j == 0 { err = fmt.Errorf("引數2不能為 %d", j) //給err變數賦值一個error物件 return //返回r和err,因為定義了返回值變數名,所以不需要在這裡寫返回變數 } return i / j, err //如果沒有賦值error給err變數,err是nil } func main() { //傳遞add函式和兩個數字,計算相加結果 n, err := foo(100, 0) if err != nil { //判斷返回的err變數是否為nil,如果不是,說明函式呼叫出錯,列印錯誤內容 println(err.Error()) } else { println(n) } }
panic可以手工呼叫,但是Golang官方建議儘量不要使用panic,每一個異常都應該用error物件捕獲。
Go語言在一些情況下會觸發內建的panic,例如 0 除、陣列越界等,修改一下上面的例子,我們讓函式引起0除panic
package main func foo(i int, j int) (r int) { return i / j } func main() { //傳遞add函式和兩個數字,計算相加結果 n := foo(100, 0) println(n) }
執行後會出現
panic: runtime error: integer divide by zero goroutine 1 [running]: main.foo(...) /lang.go:4 main.main() /lang.go:9 +0x12 exit status 2
手工panic可以這樣:
func foo(i int, j int) (r int) { if j == 0 { panic("panic說明: j為0") } return i / j }
執行後,可以看到,錯誤訊息的第一句:
panic: panic說明: j為0
物件導向
Python
Python完全支援物件導向的。
Golang
儘管Go語言允許物件導向的風格程式設計,但是本身並不是物件導向的
官方FAQ原文
Is Go an object-oriented language?
Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).
多執行緒
Python
- 使用thread模組中的start_new_thread()函式
- 使用threading模組建立執行緒
Golang
用關鍵 go建立協程goroutine
在go關鍵字後指定函式,將會開啟一個協程執行該函式。
package main import ( "fmt" "time" ) func foo() { for i := 0; i < 5; i++ { fmt.Println("loop in foo:", i) time.Sleep(1 * time.Second) } } func main() { go foo() for i := 0; i < 5; i++ { fmt.Println("loop in main:", i) time.Sleep(1 * time.Second) } time.Sleep(6 * time.Second) }
Go語言中,協程之間的通訊是通過channel實現的:
package main import ( "fmt" "time" ) //接受一個chan型別的引數c func foo(c chan int) { time.Sleep(1 * time.Second) //等待1秒 c <- 1024 //向c中寫入數字 } func main() { c := make(chan int) //建立chan變數c go foo(c) //在子寫成中執行函式foo,並傳遞變數c fmt.Println("wait chan 'c' for 1 second") fmt.Println(<-c) //取出chan 'c'的值(取值時,如果c中無值,主縣城會阻塞等待) }
總結
Python和Go分別在動態語言和靜態語言中都是最易學易用的程式語言之一。
它們並不存在取代關係,而是各自在其領域發揮自己的作用。
Python的語法簡單直觀,除了程式設計師愛不釋手外也非常適合於其他領域從業者使用。
Go兼具語法簡單和執行高效的有點,在多執行緒處理方面很優秀,非常適合已經掌握一定程式設計基礎和一門主流語言的同學學習,不過,Go是不支援物件導向的,對於大多數支援面嚮物件語言的使用者在學習Go語言的時候,需要謹記並且轉換程式設計思路。
後記
文中還有許多應該涉及的知識卻沒有能夠詳細說明,它是不完整的,甚至難免會有一些失誤。
相關文章
- 《每天用對比的思想學點 golang》PHP Python 對比 Golang 中的陣列 切片 mapGolangPHPPython陣列
- TypeScript VS JavaScript 深度對比TypeScriptJavaScript
- 客觀對比Node 與 GolangGolang
- 對比學習 ——simsiam 程式碼解析。
- Rust的Vector vs. Golang的Slice比較RustGolang
- Go vs Java vs C# 語法對比GoJavaC#
- Vue.js vs React vs Angular 深度對比Vue.jsReactAngular
- 與MSSQL對比學習MYSQL的心得MySql
- 對比學習Vue和微信小程式Vue微信小程式
- MySQL和Oracle對比學習之事務MySqlOracle
- Golang 學習——interface 介面學習(一)Golang
- Golang 學習——interface 介面學習(二)Golang
- MySQL的double write和Oracle對比學習MySqlOracle
- 開源深度學習庫對比總結深度學習
- Vue UI 框架對比 element VS iviewVueUI框架View
- Angular vs React 最全面深入對比AngularReact
- golang for range 遍歷 對比 PHP、pythonGolangPHPPython
- Python與Golang對比,如何選擇?PythonGolang
- golang string和[]byte的對比Golang
- golang 學習筆記Golang筆記
- 學習golang的迷茫Golang
- Golang學習--開篇Golang
- C#與Lua語言學習對比一C#
- Java NIO學習系列四:NIO和IO對比Java
- 學習 PySOT(2)(PySOT-toolkit、對比、畫圖)
- OSPF路由 與 ISIS路由 與路由學習對比路由
- perl DBI DBD和java 的JDBC對比學習JavaJDBC
- 阿里 vs 騰訊:全景對比(附下載)阿里
- 【學習筆記】Golang 切片筆記Golang
- GOLang 學習筆記(一)Golang筆記
- golang學習之路 之mapGolang
- goLang學習筆記(三)Golang筆記
- goLang學習筆記(四)Golang筆記
- goLang學習筆記(一)Golang筆記
- goLang學習筆記(二)Golang筆記
- golang學習第二課Golang
- golang 學習傳送門Golang
- 深入學習golang(2)—channelGolang