./read "Python 到 Go:第 2 篇 - ..."

Python 到 Go:第 2 篇 - 基础语法对比(变量、类型、函数)

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. 变量声明
  2. 基础类型
  3. 类型转换
  4. 常量
  5. 函数
  6. 控制流

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 对比总结

特性PythonGo
类型系统动态(运行时)静态(编译时)
类型推断运行时推断编译时推断
类型变更✅ 可以(不推荐)❌ 不可以
性能慢(运行时检查)快(编译时确定)⚡
错误发现运行时编译时 ✅

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
  • 字节:用 byteuint8 别名)
  • Unicode 字符:用 runeint32 别名)

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"}

字符串操作对比

操作PythonGo
长度len("hello")len("hello")
索引s[0]s[0] (返回 byte)
切片s[1:3]s[1:3]
拼接s1 + s2s1 + s2
格式化f"{name}"fmt.Sprintf("%s", name)
包含"x" in sstrings.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 函数对比总结

特性PythonGo
多返回值元组模拟原生支持 ✅
命名参数✅ 支持❌ 不支持
默认参数✅ 支持❌ 不支持(用选项模式)
变参*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 迁移清单

PythonGo备注
x = 10x := 10短变量声明
def func()func func()函数定义
return a, breturn a, b多返回值
if x:if x {花括号
for x in list:for _, x := range slice {range 遍历
"x" in sstrings.Contains(s, "x")包含判断
len(list)len(slice)长度

核心要点

  1. := 短声明:函数内最常用
  2. 多返回值:Go 的惯用模式,特别是 (result, error)
  3. 显式类型转换:所有类型转换必须显式
  4. 只有 for 循环:但足够灵活
  5. switch 无需 break:更简洁

下一篇预告

第 3 篇:数据结构与集合操作

我们将深入对比:

  • 数组 vs 切片(Slice)
  • 字典(dict) vs 映射(Map)
  • 集合(set) vs Map 模拟
  • 列表推导式 vs for 循环

🎯 动手实践

  1. 将一个简单的 Python 函数改写为 Go
  2. 尝试使用 := 和多返回值
  3. range 遍历切片

下一篇见!🚀

comments.logDiscussion Thread
./comments --show-all

讨论区

./loading comments...