Python 类型标注入门
类型注解描述接口契约,默认不在运行时强制;配合静态检查器提升可维护性。
#type / concept
#status / growing
#tech / dev
#resource / python
#tech / lang / python
[!info] 关联笔记
Python 类型标注入门
这个概念为什么出现
动态类型利于快写,但公共接口缺少契约时,重构与协作成本上升。类型标注让工具(Pyright/mypy)在运行前发现不匹配,同时保留运行时灵活。
[!abstract] 一句话理解 注解是可选的静态契约:
def f(x: int) -> str描述意图,解释器默认不检查,静态检查器与文档受益。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:定价函数明确入参出参,可选折扣
# pricing_typing.py
# 业务意图:计算折后价(分)。
# 教学点:
# - 参数/返回注解;
# - X | None 可选;
# - 注解不阻止错误实参在运行时进入(无检查器时)。
def price_after_discount(price_cents: int, discount: float | None = None) -> int:
if discount is None:
return price_cents
if not 0 <= discount <= 1:
raise ValueError("discount out of range")
return int(price_cents * (1 - discount))
def main() -> None:
print(price_after_discount(1000))
print(price_after_discount(1000, 0.1))
if __name__ == "__main__":
main()
建议运行:
python pricing_typing.py
期望输出:
1000
900
结合场景再看三个关注点
- 注解提高可读性:调用方知 discount 是 0–1 比例。
- 运行时仍靠 ValueError 守边界。
- 把
price_after_discount("1000")交给类型检查器会报警,但裸 python 可能到运算才失败。
核心概念与准确模型
| 写法 | 含义 |
|---|---|
list[str] | 字符串列表(3.9+ 内置泛型) |
dict[str, int] | 映射 |
X | Y | 联合(3.10+) |
Optional[X] | X | None |
TypeVar/Generic | 泛型库 API |
Callable[[int], str] | 可调用 |
from __future__ import annotations推迟求值typing.cast/assert/ 运行时校验库(Pydantic)是另一层
边界情况与反直觉行为
- 注解可写错而程序仍运行。
- 字符串前向引用与延迟注解。
- 动态构造难被静态模型覆盖。
常见误区
[!warning] 常见误区:有注解就不需要测试 错误理解:类型代替行为验证。
正确模型:类型抓结构,测试抓行为。
工程实践
- 公共 API 先标满
- CI 跑 pyright/mypy
- 渐进标注老代码
本节总结
类型标注是工程加速器,不是第二套运行时。与检查器一起用才完整。
自测题
- 注解会改变运行时分发吗?
float | None表达什么?
参考答案
- 默认不会。
- 要么 float,要么缺失 None。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| typing | 文档 | 类型系统库 |
| PEP 484 | PEP | Type Hints |