08.1
为什么工程化
小脚本靠动态类型很快;大型团队协作时,类型与工具链能大幅降低维护成本与回归风险。
渐进式
类型注解可选、可逐步添加。先用 mypy 检查关键模块,再逐步覆盖全仓,不必一次到位。
08.2
类型注解
用 : 标注变量、-> 标注返回值;容器用 list[int]、dict[str, int] 等泛型写法。
def total(prices: list[float]) -> float:
return sum(prices)
name: str | None = None # 3.10+ 联合语法
items: dict[str, int] = {}08.3
泛型与协议
TypeVar 定义泛型;Protocol 描述结构化接口(鸭子类型也能静态检查)。
from typing import TypeVar, Protocol
T = TypeVar("T")
def first(items: list[T]) -> T | None: return items[0] if items else None
class Sized(Protocol):
def __len__(self) -> int: ...08.4
mypy 检查
mypy 在运行前做静态类型检查,捕获类型不匹配,是 CI 的好搭档。
pip install mypy
mypy mymodule/
# mypy.ini 配置严格度
[tool.mypy]
strict = true08.5
lint 与格式化
ruff 集 lint + 格式化于一身,速度极快;black 统一格式;isort 整理导入。配置到 pre-commit。
pip install ruff
ruff check . # lint
ruff format . # 格式化08.6
项目结构
用 src/<package>/ 布局隔离包,tests/ 同级,pyproject 统一配置,避免导入歧义。
myproject/
├── pyproject.toml
├── src/mypkg/__init__.py
├── tests/test_*.py
└── README.md08.7
CI 集成
在 CI 中跑 lint + mypy + pytest,门禁合并,保证主干质量。
pre-commit
用 pre-commit 在提交时自动跑 ruff/black,把问题挡在本地,比 CI 才报错更省时。
08.8
常见陷阱
多为缺失注解或第三方库无类型存根。逐步补全注解、安装类型包(如 types-requests),用 strict 渐进收紧。
setattr/exec 等动态操作 mypy 无法推断。对这类代码加 # type: ignore 并附理由,或重构为静态可达写法。
08.9