python_到_go:第_2_篇_-_基础语法对比(变量、.md2025-10-15
./meta --show-details
Published
2025年10月15日
Reading
14 min
Words
13,169
Status
PUBLISHED
Python 到 Go:基础语法对比
📚 系列文章:第 2 篇 / 共 8 篇 🔗 上一篇:为什么选择 Go?
目录
1. 变量声明
1.1 Python 风格(动态类型)
# Python - 动态类型,无需声明类型
name = "Alice" # 自动推断为 str
age = 30 # 自动推断为 int
score = 95.5 # 自动推断为 float
is_student = True # 自动推断为 bool
# 类型注解(可选,不强制)
name: str = "Alice"
age: int = 30
# 运行时可以改变类型 ⚠️
age = "thirty" # 合法但不推荐
1.2 Go 风格(静态类型)
方式 1:完整声明
// Go - 静态类型,必须声明类型
var name string = "Alice"
var age int = 30
var score float64 = 95.5
var isStudent bool = true
方式 2:类型推断
// 类型推断(编译时确定)
var name = "Alice" // string
var age = 30 // int
var score = 95.5 // float64
var isStudent = true // bool
方式 3:短变量声明 :=
(最常用)⭐
// 短声明(只能在函数内使用)
name := "Alice" // string
age := 30 // int
score := 95.5 // float64
isStudent := true // bool
// ❌ 不能在包级别使用
// name := "Alice" // 编译错误
// ✅ 包级别必须用 var
var globalName = "Global"
1.3 对比总结
特性 | Python | Go |
---|---|---|
类型系统 | 动态(运行时) | 静态(编译时) |
类型推断 | 运行时推断 | 编译时推断 |
类型变更 | ✅ 可以(不推荐) | ❌ 不可以 |
性能 | 慢(运行时检查) | 快(编译时确定)⚡ |
错误发现 | 运行时 | 编译时 ✅ |
1.4 零值(Go 的特殊概念)
// Go 中未初始化的变量有零值(不是 null/nil)
var name string // "" (空字符串)
var age int // 0
var score float64 // 0.0
var isStudent bool // false
var ptr *int // nil(指针的零值)
fmt.Printf("name: %q\n", name) // ""
fmt.Printf("age: %d\n", age) // 0
fmt.Printf("score: %.1f\n", score) // 0.0
fmt.Printf("isStudent: %t\n", isStudent) // false
对比 Python:
# Python 未初始化变量会报错
print(name) # NameError: name 'name' is not defined
# 需要显式赋值 None
name = None
2. 基础类型
2.1 数值类型
Python:
# Python 只有 int 和 float(无限精度整数)
x = 123 # int
y = 3.14 # float
z = 1 + 2j # complex(复数)
# 大整数自动处理
big = 123456789012345678901234567890 # 无限精度
Go:
// Go 有多种整数类型(固定大小)
var i8 int8 = 127 // -128 ~ 127
var i16 int16 = 32767 // -32768 ~ 32767
var i32 int32 = 2147483647
var i64 int64 = 9223372036854775807
// 无符号整数
var u8 uint8 = 255 // 0 ~ 255
var u16 uint16 = 65535
var u32 uint32 = 4294967295
var u64 uint64 = 18446744073709551615
// 平台相关(32位系统 32位,64位系统 64位)
var i int = 100 // 最常用 ⭐
var u uint = 100
// 浮点数
var f32 float32 = 3.14
var f64 float64 = 3.14159265359 // 最常用 ⭐
// 复数
var c64 complex64 = 1 + 2i
var c128 complex128 = 1 + 2i
类型选择建议:
- 整数:默认用
int
- 浮点:默认用
float64
- 字节:用
byte
(uint8
别名) - Unicode 字符:用
rune
(int32
别名)
2.2 字符串
Python:
# Python 字符串(不可变)
s1 = "Hello"
s2 = 'World'
s3 = """多行
字符串"""
# 字符串拼接
result = s1 + " " + s2 # "Hello World"
# f-string(Python 3.6+)
name = "Alice"
age = 30
msg = f"我叫 {name},今年 {age} 岁"
# 字符串方法
upper = s1.upper() # "HELLO"
lower = s1.lower() # "hello"
contains = "ll" in s1 # True
Go:
// Go 字符串(不可变,UTF-8 编码)
s1 := "Hello"
s2 := `多行
字符串
(原始字符串)`
// 字符串拼接
result := s1 + " " + "World" // "Hello World"
// 格式化字符串(类似 f-string)
import "fmt"
name := "Alice"
age := 30
msg := fmt.Sprintf("我叫 %s,今年 %d 岁", name, age)
// 字符串包(strings 包)
import "strings"
upper := strings.ToUpper(s1) // "HELLO"
lower := strings.ToLower(s1) // "hello"
contains := strings.Contains(s1, "ll") // true
split := strings.Split("a,b,c", ",") // []string{"a", "b", "c"}
字符串操作对比:
操作 | Python | Go |
---|---|---|
长度 | len("hello") | len("hello") |
索引 | s[0] | s[0] (返回 byte) |
切片 | s[1:3] | s[1:3] |
拼接 | s1 + s2 | s1 + s2 |
格式化 | f"{name}" | fmt.Sprintf("%s", name) |
包含 | "x" in s | strings.Contains(s, "x") |
2.3 字符与字节
Python:
# Python 3:字符串是 Unicode
s = "你好"
print(len(s)) # 2 (字符数)
# 转字节
b = s.encode('utf-8') # b'\xe4\xbd\xa0\xe5\xa5\xbd'
print(len(b)) # 6 (字节数)
Go:
// Go:字符串是 UTF-8 字节序列
s := "你好"
fmt.Println(len(s)) // 6 (字节数)
// 字符数(rune 数量)
import "unicode/utf8"
fmt.Println(utf8.RuneCountInString(s)) // 2 (字符数)
// 遍历字符
for i, r := range s {
fmt.Printf("%d: %c\n", i, r)
// 0: 你
// 3: 好
}
// byte vs rune
var b byte = 'A' // uint8,ASCII 字符
var r rune = '你' // int32,Unicode 字符
3. 类型转换
3.1 Python(隐式 + 显式)
# 隐式转换
x = 10 # int
y = 3.14 # float
result = x + y # 13.14 (int 自动转为 float)
# 显式转换
a = int("123") # 123
b = float("3.14") # 3.14
c = str(123) # "123"
d = bool(0) # False
3.2 Go(仅显式转换)
// Go 不允许隐式转换 ⚠️
var x int = 10
var y float64 = 3.14
// result := x + y // ❌ 编译错误:类型不匹配
// ✅ 必须显式转换
result := float64(x) + y // 13.14
// 类型转换
import "strconv"
// 字符串 → 数值
a, err := strconv.Atoi("123") // int, error
b, err := strconv.ParseFloat("3.14", 64) // float64, error
// 数值 → 字符串
c := strconv.Itoa(123) // "123"
d := strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
为什么 Go 不允许隐式转换?
// 防止精度丢失
var i int64 = 1000000000000
var j int32 = int32(i) // 显式转换,开发者明确知道可能溢出
// Python 会自动处理,但可能隐藏问题
4. 常量
4.1 Python
# Python 没有真正的常量(约定用大写)
PI = 3.14159
MAX_SIZE = 100
# 可以修改(不推荐)
PI = 3.14 # 没有语法限制 ⚠️
4.2 Go
// Go 有真正的常量(编译时确定,不可修改)
const PI = 3.14159
const MaxSize = 100
// 常量可以不指定类型(无类型常量)
const (
Sunday = 0
Monday = 1
Tuesday = 2
Wednesday = 3
)
// iota(自增常量生成器)⭐
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
)
// iota 高级用法
const (
_ = iota // 0 (跳过)
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30 = 1073741824
)
5. 函数
5.1 基础函数
Python:
# 基础函数
def greet(name: str) -> str:
return f"Hello, {name}!"
# 调用
message = greet("Alice")
Go:
// 基础函数(参数类型在后,返回类型在最后)
func greet(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
// 调用
message := greet("Alice")
5.2 多返回值(Go 的强大特性)⭐
Python(用元组):
def divide(a: int, b: int) -> tuple[float, str]:
if b == 0:
return 0, "除数不能为零"
return a / b, ""
# 使用
result, error = divide(10, 2)
if error:
print(f"错误: {error}")
else:
print(f"结果: {result}")
Go(原生支持):
// Go 原生支持多返回值
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为零")
}
return a / b, nil
}
// 使用(惯用模式)
result, err := divide(10, 2)
if err != nil {
fmt.Printf("错误: %v\n", err)
return
}
fmt.Printf("结果: %.2f\n", result)
5.3 命名返回值
// Go 支持命名返回值(自动声明变量)
func divide(a, b float64) (result float64, err error) {
if b == 0 {
err = fmt.Errorf("除数不能为零")
return // 等价于 return result, err
}
result = a / b
return // 等价于 return result, err
}
5.4 变参函数
Python:
def sum_all(*nums: int) -> int:
return sum(nums)
result = sum_all(1, 2, 3, 4, 5) # 15
Go:
// 变参函数(...Type)
func sumAll(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
result := sumAll(1, 2, 3, 4, 5) // 15
// 展开切片
numbers := []int{1, 2, 3, 4, 5}
result := sumAll(numbers...) // 展开切片
5.5 匿名函数与闭包
Python:
# Lambda 表达式
square = lambda x: x * x
print(square(5)) # 25
# 闭包
def multiplier(factor: int):
def multiply(x: int) -> int:
return x * factor
return multiply
times2 = multiplier(2)
print(times2(5)) # 10
Go:
// 匿名函数
square := func(x int) int {
return x * x
}
fmt.Println(square(5)) // 25
// 闭包
func multiplier(factor int) func(int) int {
return func(x int) int {
return x * factor
}
}
times2 := multiplier(2)
fmt.Println(times2(5)) // 10
5.6 函数对比总结
特性 | Python | Go |
---|---|---|
多返回值 | 元组模拟 | 原生支持 ✅ |
命名参数 | ✅ 支持 | ❌ 不支持 |
默认参数 | ✅ 支持 | ❌ 不支持(用选项模式) |
变参 | *args, **kwargs | ...Type |
闭包 | ✅ 支持 | ✅ 支持 |
6. 控制流
6.1 条件语句
Python:
age = 20
if age < 18:
print("未成年")
elif age < 60:
print("成年")
else:
print("老年")
Go:
age := 20
// 标准 if
if age < 18 {
fmt.Println("未成年")
} else if age < 60 {
fmt.Println("成年")
} else {
fmt.Println("老年")
}
// if 带初始化语句(Go 特性)⭐
if score := getScore(); score >= 60 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
// score 只在 if 作用域内有效
6.2 循环
Python:
# for 循环(迭代)
for i in range(5):
print(i) # 0 1 2 3 4
# while 循环
count = 0
while count < 5:
print(count)
count += 1
# 遍历列表
fruits = ["apple", "banana", "orange"]
for fruit in fruits:
print(fruit)
# 带索引遍历
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
Go(只有 for):
// Go 只有 for 循环(但很灵活)
// 方式 1:类似 C 风格
for i := 0; i < 5; i++ {
fmt.Println(i) // 0 1 2 3 4
}
// 方式 2:类似 while
count := 0
for count < 5 {
fmt.Println(count)
count++
}
// 方式 3:无限循环
for {
// 无限循环
break // 跳出
}
// 方式 4:range 遍历 ⭐
fruits := []string{"apple", "banana", "orange"}
// 只要值
for _, fruit := range fruits {
fmt.Println(fruit)
}
// 索引和值
for i, fruit := range fruits {
fmt.Printf("%d: %s\n", i, fruit)
}
6.3 Switch 语句
Python(3.10+ 引入 match):
# Python 传统方式
def get_day_name(day: int) -> str:
if day == 1:
return "Monday"
elif day == 2:
return "Tuesday"
else:
return "Unknown"
# Python 3.10+ match 语句
match day:
case 1:
return "Monday"
case 2:
return "Tuesday"
case _:
return "Unknown"
Go:
// Go switch(无需 break)⭐
func getDayName(day int) string {
switch day {
case 1:
return "Monday" // 自动 break,不会穿透
case 2:
return "Tuesday"
default:
return "Unknown"
}
}
// 多条件
switch day {
case 1, 2, 3, 4, 5:
return "工作日"
case 6, 7:
return "周末"
default:
return "Unknown"
}
// 无条件 switch(类似 if-else)
age := 25
switch {
case age < 18:
return "未成年"
case age < 60:
return "成年"
default:
return "老年"
}
// switch 带初始化
switch score := getScore(); {
case score >= 90:
fmt.Println("优秀")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
关键差异:
- Go switch 默认不穿透(无需
break
) - Go switch 可以无条件(类似
if-else
链) - Go switch 可以带初始化语句
总结
Python → Go 迁移清单
Python | Go | 备注 |
---|---|---|
x = 10 | x := 10 | 短变量声明 |
def func() | func func() | 函数定义 |
return a, b | return a, b | 多返回值 |
if x: | if x { | 花括号 |
for x in list: | for _, x := range slice { | range 遍历 |
"x" in s | strings.Contains(s, "x") | 包含判断 |
len(list) | len(slice) | 长度 |
核心要点
:=
短声明:函数内最常用- 多返回值:Go 的惯用模式,特别是
(result, error)
- 显式类型转换:所有类型转换必须显式
- 只有 for 循环:但足够灵活
- switch 无需 break:更简洁
下一篇预告
第 3 篇:数据结构与集合操作
我们将深入对比:
- 数组 vs 切片(Slice)
- 字典(dict) vs 映射(Map)
- 集合(set) vs Map 模拟
- 列表推导式 vs for 循环
🎯 动手实践:
- 将一个简单的 Python 函数改写为 Go
- 尝试使用
:=
和多返回值 - 用
range
遍历切片
下一篇见!🚀