类型系统与常见陷阱
High Contrast
Dark Mode
Light Mode
Sepia
Forest
2 min read329 words

类型系统与常见陷阱

Python 是动态类型语言,但类型注解让代码更安全——理解可变性和常见陷阱是写出健壮代码的关键。

类型注解体系

graph TD TYPE[Python 类型系统] --> BASIC[基础类型] TYPE --> GENERIC[泛型注解] TYPE --> ADVANCED[高级类型] TYPE --> CHECK[静态检查] BASIC --> INT[int / float / str / bool] BASIC --> NONE[None / Optional] GENERIC --> LIST["list[str]"] GENERIC --> DICT["dict[str, int]"] GENERIC --> TUPLE["tuple[int, ...]"] ADVANCED --> UNION["str | int (3.10+)"] ADVANCED --> LITERAL["Literal['a', 'b']"] ADVANCED --> TYPED["TypedDict"] ADVANCED --> PROTO[Protocol] CHECK --> MYPY[mypy] CHECK --> PYRIGHT[pyright] CHECK --> RUFF[ruff] style TYPE fill:#e3f2fd,stroke:#1565c0,stroke-width:2px style ADVANCED fill:#fff3e0,stroke:#f57c00,stroke-width:2px

类型注解实战

"""
Python 类型注解:从入门到实战
"""
from typing import Optional, TypedDict, Protocol
from dataclasses import dataclass
# === 基础注解 ===
def greet(name: str, times: int = 1) -> str:
"""函数参数和返回值注解"""
return f"Hello, {name}! " * times
# 变量注解
age: int = 25
names: list[str] = ["Alice", "Bob"]
config: dict[str, int | str] = {"timeout": 30, "host": "localhost"}
# === Optional 与 None ===
def find_user(user_id: int) -> Optional[dict]:
"""可能返回 None 的函数"""
users = {1: {"name": "Alice"}, 2: {"name": "Bob"}}
return users.get(user_id)  # 找不到返回 None
# Python 3.10+ 写法
def find_user_new(user_id: int) -> dict | None:
"""等价写法(推荐)"""
return {"id": user_id} if user_id > 0 else None
# === TypedDict ===
class UserProfile(TypedDict):
"""结构化字典类型"""
name: str
age: int
email: str
is_active: bool
def create_profile(name: str, age: int) -> UserProfile:
return {
"name": name,
"age": age,
"email": f"{name.lower()}@example.com",
"is_active": True,
}
# === Protocol(结构化子类型)===
class Printable(Protocol):
"""任何有 __str__ 方法的对象"""
def __str__(self) -> str: ...
def log_item(item: Printable) -> None:
"""接受任何可打印对象"""
print(f"[LOG] {item}")
# === dataclass 替代字典 ===
@dataclass
class Config:
host: str = "localhost"
port: int = 8080
debug: bool = False
workers: int = 4
def url(self) -> str:
return f"http://{self.host}:{self.port}"
cfg = Config(host="0.0.0.0", debug=True)
print(cfg.url())  # http://0.0.0.0:8080

常见陷阱

陷阱 问题 正确做法
可变默认参数 def f(items=[]) 所有调用共享同一列表 def f(items=None)
is vs == a is b 比较的是地址不是值 值比较用 ==,仅 None 用 is
浮点精度 0.1 + 0.2 != 0.3 math.isclose()Decimal
闭包陷阱 循环中 lambda 捕获变量引用 lambda x=x: x 立即绑定
字符串拼接 循环中 += 拼接字符串 O(n²) "".join(parts)
深浅拷贝 b = a[:] 只复制一层 import copy; copy.deepcopy(a)
"""
Python 常见陷阱示例
"""
# ❌ 陷阱 1:可变默认参数
def add_item_wrong(item, items=[]):
"""Bug: 所有调用共享同一个 list"""
items.append(item)
return items
# ✅ 正确写法
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
# ❌ 陷阱 2:闭包中的循环变量
funcs_wrong = [lambda: i for i in range(5)]
print([f() for f in funcs_wrong])  # [4, 4, 4, 4, 4] 全是 4!
# ✅ 正确写法
funcs_right = [lambda i=i: i for i in range(5)]
print([f() for f in funcs_right])  # [0, 1, 2, 3, 4]
# ❌ 陷阱 3:浮点精度
print(0.1 + 0.2 == 0.3)  # False!
# ✅ 正确写法
import math
print(math.isclose(0.1 + 0.2, 0.3))  # True
# 金融计算用 Decimal
from decimal import Decimal
price = Decimal("19.99") + Decimal("0.01")
print(price)  # 20.00 精确
# ❌ 陷阱 4:字典遍历中修改
data = {"a": 1, "b": 2, "c": 3}
# for k in data:  # RuntimeError!
#     if data[k] < 2:
#         del data[k]
# ✅ 正确写法
data = {k: v for k, v in data.items() if v >= 2}
# ❌ 陷阱 5:深浅拷贝
matrix = [[1, 2], [3, 4]]
shallow = matrix[:]      # 浅拷贝:内部列表仍是引用
shallow[0][0] = 99
print(matrix[0][0])      # 99  被改了!
import copy
matrix = [[1, 2], [3, 4]]
deep = copy.deepcopy(matrix)  # 深拷贝:完全独立
deep[0][0] = 99
print(matrix[0][0])           # 1  不受影响

静态检查工具对比

工具 速度 严格度 生态 推荐场景
mypy 最成熟 大型项目标准
pyright 最高 VS Code 集成 日常开发
ruff 极快 替代 flake8+isort 快速检查
pytype Google 出品 推断类型
# 安装与使用
pip install mypy ruff
# mypy 静态类型检查
mypy src/ --strict
# ruff 代码检查 + 格式化
ruff check src/
ruff format src/

本章小结

知识点 要点
类型注解 函数签名、变量、泛型、Protocol
可变陷阱 默认参数、深浅拷贝、字典遍历
精度陷阱 浮点用 isclose,货币用 Decimal
闭包陷阱 循环 lambda 需 i=i 立即绑定
静态检查 mypy(严格) / pyright(快) / ruff(全能)

下一章:数据结构——列表、字典、集合,Python 最强大的武器。