图源:php.cn
Go语言是从C语言衍生而来,所以语言风格上是和C语言很相似的,不同的是Go在语言格式化上做的相当激进。
最显著的是在缩进和换行上,Go语言都有严格要求。此外在变量使用和包导入上,同样非常严格。
在Python中,如果引用了没有使用的包或者是有变量没有使用,是不会影响到程序的执行的,比如:
from typing import NamedTuple
message = 'How are you!'
print('hello world')
# hello world
其中的包NamedTuple
和变量message
都是不必要的,但这些都不会阻止程序的正常执行。当然可能会造成性能浪费,优秀的IDE也会通过颜色高亮来提醒你这些是可以删除的部分。但无论怎么说,大多数编程语言(如C++/Java/PHP等)都不会强制要求开发者剔除不必要的引用或者变量,但Go语言会:
package main
import "fmt"
func main() {
message := "How are you!"
fmt.Printf("Hello World")
}
// Build Error: go build -o C:\Users\70748\AppData\Local\Temp\__debug_bin2433955485.exe -gcflags all=-N -l .\hello.go
// # command-line-arguments
// .\hello.go:6:2: message declared but not used (exit status 2)
这里message
变量虽然声明并初始化,但在后续程序中并没有真正使用,所以是无效的代码,这里直接会被编译器提示message declared but not used
进而中断编译过程。事实上无效的导入也会造成同样的后果。
不过导入方面的问题不需要开发者操心,Go提供官方自动导入工具,VSC之类的IDE会自动调用该工具导入需要的包或者删除不必要的包。
在这方面Go语言是相当极端的,这样做优点和缺点同样明显。
优点是保证代码的干净高效,剔除不必要的无效代码,且可以在编译阶段发现一些不必要的bug,同时保持一致性的代码风格,让Go语言源码都具有很高的可读性。缺点是扼杀了代码的个性,可能一部分程序员初次接触后会相当的不习惯。
Go语言脱胎于C,且因为同样是静态的强类型语言,所以变量声明风格是极为类似的:
package main
import "fmt"
func main() {
var a int = 1
var b int = 2
var c int = a + b
fmt.Println("the result is:", c)
}
// the result is: 3
当然我个人认为是不如int a = 1
这样的方式简洁的。不过在形式上倒是颇类似使用注解后的Python:
a: int = 1
b: int = 2
c: int = a+b
print("the result is :", c)
# the result is : 3
除了上面的方式之外,Go语言还支持一种简短写法:
package main
import "fmt"
func main() {
a := 1
b := 2
c := a + b
fmt.Println("the result is:", c)
}
// the result is: 3
通过使用:=
操作符,可以在声明并初始化变量的时候,根据初始化的值的类型来自动推断出变量的类型,并将变量声明为相应类型后初始化。所以a := 1
与var a int = 1
是等效的。
有趣的是
:=
在Python中是赋值表达式使用的符号,还有个很别致的名称叫做“海象操作符”,但用法有所不同,因为Python并不存在所谓的类型推断,因为类型推断存在于Python本身的机制中,是Python的一部分,是对开发者不可见的。
除了上边通常的对变量“声明并初始化”的做法之外,变量是可以单独声明的并且不进行显式地初始化的,这样做并不会有C或C++中的“脏数据”的风险,因为和其它诸如Java之类的现代语言类似,Go语言会在变量声明的同时进行隐式初始化:
package main
import "fmt"
func main() {
var integer int
var text string
fmt.Println("integer is ", integer)
fmt.Println("text is ", text)
}
// integer is 0
// text is
在这个例子中int
类型的变量integer
被初始化为0
,字符串类型的变量text
被初始化为空字符串。此外bool
类型的变量会被初始化为False
。
在Go语言中定义函数和在Python中定义虽然不能说一模一样,但也可以说是极为相似了:
package main
import "fmt"
func main() {
a := 1
b := 10
result := add(a, b)
fmt.Println("result is :", result)
}
func add(a int, b int) int {
return a + b
}
// result is : 11
Python中:
def add(a: int, b: int) -> int:
return a+b
a: int = 1
b: int = 2
c: int = add(a, b)
print("the result is :", c)
# the result is : 3
不同的是,Go语言有个可以说是“堵门绝技”一般的函数功能:多返回值。
一般来说,函数都只能返回一个返回值,如果需要返回多个数据,都是用对象或者一些内建容器作为返回值来完成。但Go语言可以直接返回多个返回值:
package main
import "fmt"
func main() {
a := 10
b := 2
add_result, sub_result, mul_result, div_result := four_fundamental(a, b)
fmt.Println("add_result is :", add_result)
fmt.Println("sub_result is :", sub_result)
fmt.Println("mul_result is :", mul_result)
fmt.Println("div_result is :", div_result)
}
func four_fundamental(a int, b int) (int, int, int, int) {
return a + b, a - b, a * b, a / b
}
// add_result is : 12
// sub_result is : 8
// mul_result is : 20
// div_result is : 5
其实这种做法和Python中返回一个元组的做法类似:
def four_fundamental(a: int, b: int) -> tuple:
return (a+b, a-b, a*b, int(a/b))
a: int = 10
b: int = 2
add_result: int
sub_result: int
mul_result: int
div_result: int
add_result, sub_result, mul_result, div_result = four_fundamental(a, b)
print("add_result is :", add_result)
print("sub_result is :", sub_result)
print("mul_result is :", mul_result)
print("div_result is :", div_result)
# add_result is : 12
# sub_result is : 8
# mul_result is : 20
# div_result is : 5
Python中的变量类型注解是不能在解包的时候写在同一行的,详情见[PEP 526 – Syntax for Variable Annotations](https://github.com/icexmoon/PEP-CN/blob/main/peps/PEP 526 – Syntax for Variable Annotations.md),当然注解并非是Python编程所必须的。
此外,如果不需要使用某个函数的返回值,可以使用空白标识符_
替代:
package main
import "fmt"
func main() {
a := 10
b := 2
add_result, _, _, _ := four_fundamental(a, b)
fmt.Println("add_result is :", add_result)
}
func four_fundamental(a int, b int) (int, int, int, int) {
return a + b, a - b, a * b, a / b
}
// add_result is : 12
Python程序员应该对此很熟悉,毕竟
_
也在Python中大量作为空白标识符被使用。甚至最新的Python3.10中的新功能模式匹配中也巧妙运用case _:
实现了类似switch...case
语句中的default
的作用,详情见[PEP 636 – Structural Pattern Matching: Tutorial](https://github.com/icexmoon/PEP-CN/blob/main/peps/PEP 636 – Structural Pattern Matching Tutorial.md)。