Python类型注解仅是声明意图的元数据,运行时不生效;mypy等工具在运行前静态检查类型一致性,而运行时校验需typeguard等额外库支持。

为什么 typing 注解本身不触发运行时类型检查
Python 的类型注解(如 def greet(name: str) -> str:)只是元数据,解释器默认完全忽略它们。即使传入 greet(42),函数照样执行——这常让刚接触 typing 的人误以为“写了注解就等于校验了”。真正做静态检查的是外部工具,最常用的是 mypy,它在代码运行前扫描 AST,比对注解与实际使用是否一致。
关键点:typing 是“声明意图”,mypy 是“验证意图”,二者职责分离。不装 mypy 或不运行它,注解就只是注释。
如何用 mypy 检查单个函数的参数类型
假设你写了这个函数:
def process_id(user_id: int) -> str:
return f"user_{user_id}"
保存为 main.py 后,在终端运行:
立即学习“Python免费学习笔记(深入)”;
-
mypy main.py—— 检查整个文件 -
mypy --disallow-untyped-defs main.py—— 强制所有函数必须有完整注解(推荐开启) -
mypy --check-untyped-args main.py—— 对未注解参数也报错(更严格)
若调用处写 process_id("abc"),mypy 会立刻报错:Argument 1 to "process_id" has incompatible type "str"; expected "int"。注意:错误发生在调用点,不是函数定义处。
Union、Optional 和字面量类型的实际写法差异
常见误区是把运行时判断逻辑和类型声明混为一谈。比如想接受 int 或 None,不能写 def foo(x: int or None):(语法错误),而要明确用类型组合:
- 多选一:
from typing import Union,然后写x: Union[int, str];Python 3.10+ 可简写为x: int | str - 可为空:
from typing import Optional,x: Optional[int]等价于Union[int, None],但更清晰 - 精确值限制:
from typing import Literal,如mode: Literal["read", "write"],mypy会拒绝"append"
这些类型只影响 mypy 推导,不改变运行时行为——Optional[int] 不会自动把 None 转成 0,也不阻止你传字符串。
运行时强制校验需要额外库,typing + mypy 做不到
如果你需要函数在运行时抛出 TypeError(比如 API 入口校验),typing 和 mypy 都无能为力。此时得引入运行时检查库:
-
typeguard:装饰器@typechecked可实时校验注解 -
pydantic:适合结构化数据,用BaseModel定义参数再解析 - 手写
isinstance()判断:简单场景够用,但重复代码多
混淆这两类检查是最大陷阱——以为加了 -> List[str] 就能防止返回 None,结果上线后崩在生产环境。静态检查和运行时检查必须按需并存,不能互相替代。


















