为什么要静态类型检查
注解光写不查等于没写。mypy 在不运行代码的情况下扫描,提前发现:
- 把 None 当 str 用
- 函数调用参数类型错
- 字典 key 拼写错
- 忘处理 None 分支
装 + 跑
pip install mypy
mypy myfile.py
mypy mypackage/
第一个例子
# bad.py
def greet(name: str) -> str:
return f"hi {name}"
greet(42) # 传了 int 不是 str
$ mypy bad.py
bad.py:4: error: Argument 1 to "greet" has incompatible type "int"; expected "str"
Found 1 error
常见报错与修法
1. Optional 没处理
def find(uid: int) -> str | None:
return DB.get(uid)
name = find(1)
print(name.upper()) # mypy: Item "None" of "str | None" has no attribute "upper"
修复:
name = find(1)
if name is not None:
print(name.upper())
2. Any 像传染病
import json
data = json.loads("...") # data 是 Any
result = data.upper() # 通过!但运行可能崩
加上 TypedDict 或显式类型:
from typing import TypedDict
class Config(TypedDict):
name: str
port: int
data: Config = json.loads(...)
3. 容器内类型不一致
nums: list[int] = [1, 2, "3"] # mypy: Incompatible types
配置文件 mypy.ini
[mypy]
python_version = 3.12
strict = True
warn_unused_ignores = True
warn_redundant_casts = True
# 第三方库没有类型时不报警
[mypy-some_lib.*]
ignore_missing_imports = True
strict = True 启用最严格的所有检查。
# type: ignore 暂时忽略
import flaky_lib # type: ignore[import]
result = call_external() # type: ignore # 知道有问题,但暂时跳过
reveal_type:调试神器
x = some_complex_expression
reveal_type(x) # mypy 会打印推断出的类型
只在 mypy 检查时有效,运行时会报错——所以临时用,用完删掉。
pyright:另一个选择
pyright 是微软出的,集成在 VS Code 的 Pylance 插件里。比 mypy 快,编辑器实时报错。
pip install pyright
pyright myfile.py
VS Code 用户:装 Pylance 插件就内置了。
渐进式接入
老项目不可能一次全标。策略:
- 先开
--strict跑新模块 - 老模块用
# type: ignore整个文件忽略 - 一个文件一个文件地加注解
实际收益
- IDE 自动补全更准
- 重构时改了 A 函数,调用方有问题立刻看到
- 团队协作减少"这个 None 怎么来的"扯皮
下一篇讲 dataclass / TypedDict / NamedTuple——现代数据载体。