Golang和Python都是目前在各自領域最流行的開發語言之一。python
Golang其高效而又友好的語法,贏得了不少後端開發人員的青睞,最適用於高併發網絡編程的語言之一。程序員
Python不用說,TIOBE排行榜的前十常駐居民,如今已經穩定在前五了。在機器學習、AI、數據分析領域成爲必學語言。shell
兩門編程語言在語法上都有各自的特色,並且都易學易用。編程
本文對比這兩門語言目的不是爭誰優誰略,只是爲了對比學習,適合掌握Python想學Go或者掌握Go想學Python的同窗們參考。後端
Go和Python,一個是靜態語言一個是動態語言,從各個方面來看,都有根本性的差別,因此,文中不少內容不進行深刻的比較了,咱們只從程序員最直觀的語法面作對比。數組
爲了便於閱讀,文中涉及代碼都採用儘可能簡單的語句呈現網絡
Python中默認的編碼格式是 ASCII 格式,程序文件中若是包含中文字符(包括註釋部分)須要在文件開頭加上 # -*- coding: UTF-8 -*-
或者 #coding=utf-8
就好了多線程
原生支持Unicode併發
30個關鍵字app
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
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是動態語言,因此在定義變量的時候不須要申明類型,直接使用便可。
Python會根據值判斷類型。
name = "Zeta" # 字符串變量 age = 38 # 整數 income = 1.23 # 浮點數
多變量賦值
a,b = 1,2 # a=1; b=2 c = d = 3 # c=3; d=3
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中的List列表對應Go語言中的Slice切片
Python中的Dictionary字典對應Go語言中的map
有一些值得注意的地方:
Python類型轉換很是簡單,用類型名做爲函數名便可。
int(n) # 將數字n轉換爲一個整數 float(n) # 將數字n轉換到一個浮點數 str(o) # 將對象 obj 轉換爲字符串 tuple(s) # 將序列 s 轉換爲一個元組 list(s) # 將序列 s 轉換爲一個列表 set(s) # 將序列 s 轉換爲一個集合
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傳統的判斷語句以下
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
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中有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
試試看。)
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) }
用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 返回函數結果。
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中,使用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
也是使用import
導入包,導入包指定的是包的路徑,包名默認爲路徑中的最後部分
import "net/url" //導入url包
多個包能夠用()
組合導入
import ( "fmt" "net/url" )
爲導入的包設置別名, 直接在導入包時,直接在報名前面添加別名,用空格隔開
import ( f "fmt" u "net/url" )
Python中用經典的 try/except
捕獲異常
try: <語句> #運行別的代碼 except <異常名稱>: <語句> # except <異常名稱>,<數據>: <語句> #若是引起了指定名稱的異常,得到附加的數據
還提供了 else 和 finally
若是沒發生異常的執行else
語句塊,finally
塊的代碼不管是否捕獲異常都會執行
Python內建了很全面的異常類型名稱,同時能自定義異常類型
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徹底支持面向對象的。
儘管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).
thread
模塊中的start_new_thread()
函數threading
模塊建立線程用關鍵 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語言的時候,須要謹記而且轉換編程思路。
文中還有許多應該涉及的知識卻沒有可以詳細說明,它是不完整的,甚至不免會有一些失誤。
若是,您以爲本文對您有所幫助,但願可以獲得您的點贊支持,若是文中有錯誤的地方,也但願不吝賜教,經過評論或者公衆號和你們一塊兒交流學習。