天天看點

golang對比python

golang對比python

Golang和Python都是目前在各自領域最流行的開發語言之一。

Golang其高效而又友好的文法,赢得了很多後端開發人員的青睐,最适用于高并發網絡程式設計的語言之一。

Python不用說,TIOBE排行榜的前十常駐居民,現在已經穩定在前五了。在機器學習、AI、資料分析領域成為必學語言。

兩門程式設計語言在文法上都有各自的特點,而且都易學易用。

本文對比這兩門語言目的不是争誰優誰略,隻是為了對比學習,适合掌握Python想學Go或者掌握Go想學Python的同學們參考。

Go和Python,一個是靜态語言一個是動态語言,從各個方面來看,都有根本性的差異,是以,文中很多内容不進行深入的比較了,我們隻從程式員最直覺的文法面做對比。

為了便于閱讀,文中涉及代碼都采用盡量簡單的語句呈現

字元編碼

Python

Python2中預設的編碼格式是 ASCII 格式,程式檔案中如果包含中文字元(包括注釋部分)需要在檔案開頭加上 ​

​# -*- coding: UTF-8 -*-​

​​ 或者 ​

​#coding=utf-8​

​ 就行了

Python3預設支援Unicode

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提供兩種錯誤處理方式

  1. 函數傳回​

    ​error​

    ​類型對象判斷錯誤
  2. ​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

  1. 使用​

    ​thread​

    ​​子產品中的​

    ​start_new_thread()​

    ​函數
  2. 使用​

    ​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分别在動态語言和靜态語言中都是最易學易用的程式設計語言之一。