对比学习:Golang VS Python3,帮助 Python 快速了解 Go

Golang 和 Python 都是目前在各自领域最流行的开发语言之一。

Golang 其高效而又友好的语法,赢得了很多后端开发人员的青睐,最适用于高并发网络编程的语言之一。Python 不用说,TIOBE 排行榜的前十常驻居民,现在已经稳定在前五了。在机器学习、AI、数据分析领域成为必学语言。

两门编程语言在语法上都有各自的特点,而且都易学易用。Go 和 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    notassert    finally orbreak    for passclass    from    printcontinue global    raisedef    if  returndel    import  tryelif    in  whileelse    is  withexcept    lambda  yield复制代码
Golang

25 个关键字

break    default func    interface   selectcase    defer   go  map structchan    else    goto    package switchconst    fallthrough if  range   typecontinue    for import  return  var复制代码

注释

Python
# 单行注释'''多行注释多行注释'''"""多行注释多行注释"""复制代码
Golang
//单行注释/*多行注释多行注释*/复制代码

变量赋值

Python

Python 是动态语言,所以在定义变量的时候不需要申明类型,直接使用即可。 Python 会根据值判断类型。

name = "Zeta" # 字符串变量age = 38 # 整数income = 1.23 # 浮点数复制代码

多变量赋值

a,b = 1,2 # a=1; b=2c = d = 3 # c=3; d=3复制代码
Golang

Go 是静态语言,是强类型的,但是 Go 语言也允许在赋值变量时确定类型。

因此 Go 有多种申明变量的方式

// 1\. 完整的申明并赋值var a inta = 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 := 1024f := float32(i)i = float32(f)复制代码

另外,Python 中可以直接转换数字字符串和数字:

s = "123"i = 456print(int(s), str(i))复制代码

但是 Go 是不可以的。

Go 语言的字符串处理很不同,string()只能用于[]byte类型转换成字符串,其他基础类型的转换需要用strconv包,另外,其他类型转换成为string类型除了用strconv包,还可以用fmt.Sprintf函数:

package mainimport (    "fmt"    "strconv")func main() {    s := "123"    i, _ := strconv.Atoi(s)    println(i)    s2 := fmt.Sprintf("%d", 456)    println(s2)}复制代码

Go 中的 interface 类型是不能直接转换成其他类型的,需要使用到断言

package mainfunc main() {var itf interface{} = 1i, 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 中有whilefor两种循环,都可以使用break跳出循环和continue立即进入下一轮循环,另外,Python 的循环语句还可以用else执行循环全部完毕后的代码,break跳出后不会执行else的代码

while 条件循环,

count = 0while (count < 9):    print('The count is:', count)    count = count + 1    if count == 5:        break   # 可以比较以下 break 和不 break 的区别        passelse:    print('loop over')复制代码

for 遍历循环,循环遍历所有序列对象的子项

names = ['zeta', 'chow',  'world']for n in names:    print('Hello, ' + n)    if n == 'world':        break        passelse:    print('Good night!')复制代码

for循环中也可以用else,(注释掉代码中的break试试看。)

Golang

Go 语言只有一个循环语句for,但是根据不同的表达式,for有不同的表现

for 前置表达式; 条件表达式; 后置表达式 {    //...}复制代码

前置表达式 在每轮循环前运行,可以用于声明变量或调用函数返回; 条件表达式 满足该表达式则执行下一轮循环,否则退出循环; 后置表达式 在循环完成后执行

经典的用法:

for i := 0; i < 10; i++ {    println(i)}复制代码

我们可以忽略掉前置和后置表达式

sum := 1for 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)    passfoo("zeta")复制代码

默认参数 Python 定义函数参数时,可以设置默认值,调用时如果没有传递该参数,函数内将使用默认值,默认值参数必须放在无默认值参数后面。

def foo(name="zeta"):    print("hello, "+name)    passfoo()复制代码

关键字参数 一般函数传递参数时,必须按照参数定于的顺序传递,但是 Python 中,允许使用关键字参数,这样通过指定参数明,可以不按照函数定义参数的顺序传递参数。

def foo(age, name="zeta"):    print("hello, "+name+"; age="+str(age))    passfoo(name="chow", age=18)复制代码

不定长参数,Python 支持不定长参数,用*定义参数名,调用时多个参数将作为一个元祖传递到函数内

def foo(*names):    for n in names:        print("hello, "+n)    passfoo("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 worlddefer 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 maintype 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)}复制代码

结果

32复制代码

模块

Python
  • 模块是一个.py 文件
  • 模块在第一次被导入时执行
  • 一个下划线定义保护级变量和函数,两个下划线定义私有变量和函数
  • 导入模块习惯性在脚本顶部,但是不强制
Golang
  • 与文件和文件名无关,每一个文件第一行用 package 定义包名,相同包名为一个包
  • 包中的变量第一次引用时初始化,如果包中包含 init 函数,也会在第一次引用时执行(变量初始化后)
  • 保重首写字母大写的函数和变量为共有,小写字母为私有,Golang 不是面向对象的,所以不存在保护级。
  • 导入模块必须写在 package 之后,其他代码之前。

导入包

Python

在 Python 中,使用import导入模块。

#!/usr/bin/python# -*- coding: UTF-8 -*-# 导入模块import supportsupport.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 <异常名称>,<数据>:<语句>        #如果引发了指定名称的异常,获得附加的数据复制代码

还提供了 elsefinally

如果没发生异常的执行else语句块,finally块的代码无论是否捕获异常都会执行

Python 内建了很全面的异常类型名称,同时能自定义异常类型

Golang

Golang 里没有用经典的 try/except捕获异常。

Golang 提供两种错误处理方式

  1. 函数返回error类型对象判断错误
  2. panic异常

一般情况下在 Go 里只使用error类型判断错误,Go 官方希望开发者能够很清楚的掌控所有的异常,在每一个可能出现异常的地方都返回或判断error是否存在。

error是一个内置的接口类型

type error interface {    Error() string}复制代码

通常,使用error异常处理类似这样:

package mainimport "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 mainfunc 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 zerogoroutine 1 [running]:main.foo(...)        /lang.go:4main.main()        /lang.go:9 +0x12exit 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 mainimport (    "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 mainimport (    "fmt"    "time")//接受一个 chan 类型的参数 cfunc 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 语言的时候,需要谨记并且转换编程思路。

后记

文中还有许多应该涉及的知识却没有能够详细说明,它是不完整的,甚至难免会有一些失误。

如果,您觉得本文对您有所帮助,希望能够得到您的点赞支持,如果文中有错误的地方,也希望不吝赐教,通过评论或者公众号和大家一起交流学习。


本文首发于 GitChat,未经授权不得转载,转载需与 GitChat 联系。

阅读全文: http://gitbook.cn/gitchat/activity/5d686f7546df0a47de6ade3a

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值