类型系统与类型推断
本章深入Go类型系统:静态类型vs动态类型、强类型与零值机制、类型别名与自定义类型、类型转换与断言。重点:Go类型安全是编译时保证,理解零值机制可避免很多bug。
前置知识:第3章 语法基础 学习目标:理解Go类型系统差异、掌握类型转换/断言用法、学会用自定义类型增加安全性
4.1 静态类型 vs 动态类型
Section titled “4.1 静态类型 vs 动态类型”Python的动态类型
Section titled “Python的动态类型”# Python: 类型在运行时决定x = 10 # intprint(type(x)) # <class 'int'>
x = "hello" # 变成strprint(type(x)) # <class 'str'>
x = [1, 2, 3] # 变成listprint(type(x)) # <class 'list'>
# 函数参数无类型约束def process(value): return value * 2
print(process(5)) # 10print(process("ab")) # "abab"print(process([1])) # [1, 1]Go的静态类型
Section titled “Go的静态类型”// Go: 类型在编译时决定var x int = 10fmt.Println(reflect.TypeOf(x)) // int
// x = "hello" // 编译错误:无法将string赋值给int
// 函数参数有类型约束func process(value int) int { return value * 2}
fmt.Println(process(5)) // 10// fmt.Println(process("ab")) // 编译错误:无法将string作为int传入Go支持类型推断,Python也支持(但机制不同):
// Go: 编译器推断类型x := 10 // inty := 3.14 // float64z := "hello" // stringb := true // bool
// 切片推断items := []int{1, 2, 3} // []int# Python: 动态类型,无需推断x = 10 # inty = 3.14 # floatz = "hello" # strb = True # bool
# 列表推断items = [1, 2, 3] # list编译时检查的优势
Section titled “编译时检查的优势”// 错误在编译时被捕获func getUser(id int) User { return users[id] // 如果users不是map,编译错误}
// Python错误在运行时def get_user(id): return users[id] # 如果users不是字典,运行时错误类型系统对比
Section titled “类型系统对比”| 特性 | Python | Go |
|---|---|---|
| 类型检查 | 运行时 | 编译时 |
| 变量类型 | 可变 | 固定 |
| 函数参数类型 | 可变 | 固定(可推断) |
| 类型推断 | 无需(动态) | 支持 |
| 泛型 | 无 | 支持(Go 1.18+) |
4.2 强类型与零值机制
Section titled “4.2 强类型与零值机制”本节目的:理解Go强类型约束和零值机制,对比Python的None
Go是强类型语言,类型转换必须显式进行:
var i int = 10var f float64 = i // 编译错误:类型不匹配
// 正确做法:显式类型转换var f float64 = float64(i)Python相对宽松,但也需要显式转换:
i = 10f = float(i) # 必须显式转换Go的变量初始化有默认值:
var i int // 0var f float64 // 0.0var b bool // falsevar s string // ""(空字符串)var ptr *int // nilvar slice []int // nilvar m map[string]int // nil
// struct的零值type User struct { Name string Age int}var user User // Name="" Age=0Python无零值机制
Section titled “Python无零值机制”# Python变量必须初始化# 使用前必须赋值,否则NameError
name = "" # 需要手动初始化age = 0
# 类实例class User: def __init__(self): self.name = "" # 手动初始化 self.age = 0nil vs 空值
Section titled “nil vs 空值”// Go: nil表示"无"var s []string = nil // 切片为空var m map[string]int = nil // map为空var p *int = nil // 指针为空
// 判断nilif s == nil { fmt.Println("切片为空")}
// 注意:nil切片和空切片不同var s1 []int = nil // len=0, cap=0s2 := []int{} // len=0, cap=0s3 := make([]int, 0) // len=0, cap=0
// s1 == nil 为true// s2 == nil 为false(空切片)# Python: None表示"无"s = None # 列表为空s = [] # 空列表m = None # 字典为空m = {} # 空字典
# 判断if s is None: print("为空")if s == []: print("空列表")零值的实际应用
Section titled “零值的实际应用”// 使用零值可以安全地使用变量func getScore(name string) int { scores := map[string]int{ "Alice": 95, "Bob": 80, } // 如果name不存在,返回int的零值0 return scores[name] // 安全,不会报错}
// 对比Pythondef get_score(name): scores = { "Alice": 95, "Bob": 80, } return scores.get(name, 0) # 需要使用get并提供默认值4.3 类型别名与自定义类型
Section titled “4.3 类型别名与自定义类型”本节目的:区分类型别名(
type A = int)与自定义类型(type A int),理解自定义类型的价值
// byte是uint8的别名type Byte = uint8type Rune = int32
var b Byte = 255var r Rune = 100
// 它们可以互相赋值var u uint8 = b // 合法类型定义(新类型)
Section titled “类型定义(新类型)”// 定义新类型(不是别名)type ID inttype UserName string
var id ID = 12345var name UserName = "Alice"
// id和int不兼容var n int = id // 编译错误
// 需要显式转换var n int = int(id)1. 增加类型安全性
type UserID inttype OrderID int
func getUser(id UserID) { fmt.Printf("User ID: %d\n", id)}
var uid UserID = 100var oid OrderID = 200
getUser(uid) // OK// getUser(oid) // 编译错误:参数类型不匹配2. 给已有类型添加方法
type ID int
func (i ID) String() string { return fmt.Sprintf("ID:%d", i)}
var id ID = 123fmt.Println(id.String()) // ID:123Python的等效概念
Section titled “Python的等效概念”# 类型别名UserId = intOrderId = int
# 类型约束(运行时)class UserId: def __init__(self, value): if not isinstance(value, int): raise TypeError("must be int") self.value = value
# Python无法在编译时强制类型约束4.4 类型转换 vs 类型断言
Section titled “4.4 类型转换 vs 类型断言”本节目的:区分类型转换(编译时、显式)和类型断言(运行时、接口)
类型转换(Type Conversion)
Section titled “类型转换(Type Conversion)”将A类型转为B类型:
// 整数之间var i int = 10var i64 int64 = int64(i)var i8 int8 = int8(i) // 可能溢出
// 浮点转整数(截断)var f float64 = 3.14var i int = int(f) // 3
// 数值转字符串str := strconv.Itoa(123)str := fmt.Sprintf("%d", 123)
// 字符串转数值i, err := strconv.Atoi("123")f, err := strconv.ParseFloat("3.14", 64)
// 字节切片转字符串bytes := []byte{'h', 'e', 'l', 'l', 'o'}s := string(bytes)
// 字符串转字节切片s := "hello"b := []byte(s)Python对应
Section titled “Python对应”# 整数之间i = 10i64 = int(i) # Python int是任意精度
# 浮点转整数f = 3.14i = int(f) # 3
# 数值转字符串s = str(123)
# 字符串转数值i = int("123")f = float("3.14")
# 字节和字符串b = "hello".encode('utf-8')s = b.decode('utf-8')类型断言(Type Assertion)
Section titled “类型断言(Type Assertion)”从接口类型提取具体类型:
// 接口类型var i interface{} = "hello"
// 类型断言s, ok := i.(string)if ok { fmt.Println(s) // hello}
// 失败情况var i interface{} = 123s, ok := i.(string)if !ok { fmt.Println("不是字符串") // 执行这里}
// 安全断言(可能panic)s := i.(string) // 如果不是string,panic类型断言用于类型分支
Section titled “类型断言用于类型分支”func describe(i interface{}) { switch v := i.(type) { case int: fmt.Printf("int: %d\n", v) case string: fmt.Printf("string: %s\n", v) case float64: fmt.Printf("float64: %f\n", v) default: fmt.Printf("unknown: %T\n", v) }}
describe(123) // int: 123describe("hello") // string: hellodescribe(3.14) // float64: 3.140000describe([]int{1, 2}) // unknown: []intPython的type检查
Section titled “Python的type检查”def describe(i): if isinstance(i, int): print(f"int: {i}") elif isinstance(i, str): print(f"string: {i}") elif isinstance(i, float): print(f"float: {i}") else: print(f"unknown: {type(i)}")
describe(123)describe("hello")断言失败处理
Section titled “断言失败处理”// 方法一:ok模式value, ok := interface{}(123).(string)if !ok { fmt.Println("转换失败,使用默认值") value = "default"}
// 方法二:switch typeswitch v := i.(type) {case string: fmt.Println("字符串:", v)default: fmt.Println("非字符串类型")}- 自定义类型增加安全性:用
type UserID int代替int,防止UserID和OrderID混用 - 类型断言用ok模式:除非确定类型,否则不要直接
i.(string),用value, ok := i.(string) - 空接口
interface{}慎用:Go 1.18+可用any代替,但泛型是更好的选择 - 类型转换必须显式:Go不会像Python那样自动转换,编译时会报错
- nil不等于零值:指针、slice、map、channel、interface的零值是
nil,不是”空”而是”未初始化”
- 静态类型:Go在编译时检查类型,Python在运行时
- 零值机制:Go所有变量有默认值(0, "", false, nil),Python必须初始化
- 类型别名:
type Alias = ExistingType,类型安全增强 - 类型转换:
Type(value)显式转换 - 类型断言:
value, ok := i.(Type)从接口提取值
| Python | Go |
|---|---|
| 动态类型 | 静态类型 |
| 无零值 | 零值机制 |
| isinstance | type switch / 类型断言 |
| 无 | type alias: type ID = int |
| 无 | 新类型: type ID int |
- 声明不同类型的变量,打印它们的零值
- 创建UserID和OrderID类型,写函数防止混用
- 实现字符串转浮点数的错误处理
- 用类型断言从
interface{}提取不同类型的值 - 写一个函数,使用type switch打印任意值类型