类型提示与静态检查
学习目标
- 为变量、函数和集合写 Python 3.11+ 类型提示。
- 使用
TypeAlias、TypedDict、Literal和Protocol表达边界。 - 区分静态检查、运行时检查和数据校验。
核心知识点
类型提示默认不会改变 Python 的运行时行为。它服务于 IDE、mypy、pyright 等工具,帮助发现参数和返回值不一致。运行时仍要对文件、网络和用户输入做显式校验。
常用用法
变量与函数注解
python
def average(values: list[float]) -> float:
return sum(values) / len(values)
score: float = average([80.0, 90.0])
print(score)
# 输出:85.0Python 3.9+ 可直接写 list[str];函数返回类型应与所有正常返回路径一致。
可选值与联合类型
python
def find_name(user_id: int) -> str | None:
return "Ada" if user_id == 1 else None
name = find_name(2)
print(name is None)
# 输出:TrueT | None 表示可能缺失,调用者在使用字符串方法前应先缩小类型。
类型别名
python
from typing import TypeAlias
UserId: TypeAlias = int
Coordinates: TypeAlias = tuple[float, float]
point: Coordinates = (1.0, 2.0)
user_id: UserId = 7
print(point, user_id)
# 输出:(1.0, 2.0) 7别名给复杂类型一个领域名称,不会创建新的运行时类型。
TypedDict
python
from typing import TypedDict
class UserRecord(TypedDict):
name: str
active: bool
user: UserRecord = {"name": "Ada", "active": True}
print(user["name"])
# 输出:AdaTypedDict 描述字典的键结构,运行时仍然是普通 dict,不会自动检查字段。
Literal
python
from typing import Literal
def set_mode(mode: Literal["safe", "fast"]) -> str:
return mode
print(set_mode("safe"))
# 输出:safeLiteral 适合有限字符串或整数选项,静态检查器可以发现拼写错误。
Callable
python
from collections.abc import Callable
def apply(value: int, transform: Callable[[int], int]) -> int:
return transform(value)
print(apply(3, lambda value: value * 2))
# 输出:6优先从 collections.abc 导入现代可调用协议;需要表达复杂回调时给参数和返回值写完整类型。
泛型函数
python
from typing import TypeVar
T = TypeVar("T")
def first(values: list[T]) -> T:
return values[0]
print(first(["a", "b"]), first([1, 2]))
# 输出:a 1泛型表达“输入和输出保持同一种类型”,比写 Any 更能保留检查信息。
不常用但需要知道
Protocol
python
from typing import Protocol
class HasName(Protocol):
name: str
def display(value: HasName) -> str:
return value.name
class User:
name = "Ada"
print(display(User()))
# 输出:Ada协议使用结构化子类型:对象只要具备所需成员即可,不必显式继承。
Final
python
from typing import Final
MAX_RETRIES: Final = 3
print(MAX_RETRIES)
# 输出:3Final 提醒静态检查器禁止重新绑定,不会在运行时阻止赋值。
TypeGuard
python
from typing import TypeGuard
def is_str_list(value: list[object]) -> TypeGuard[list[str]]:
return all(isinstance(item, str) for item in value)
items: list[object] = ["a", "b"]
print(is_str_list(items))
# 输出:True类型守卫把运行时判断结果告诉静态检查器,必须保证谓词实现确实可靠。
typing.get_type_hints()
python
from typing import get_type_hints
def add(left: int, right: int) -> int:
return left + right
print(get_type_hints(add))
# 输出:{'left': <class 'int'>, 'right': <class 'int'>, 'return': <class 'int'>}反射类型提示可能触发前向引用解析,不要把它当作完整运行时验证器。
易混点
Any会关闭很多静态检查,只有确实无法描述的边界才使用。list[str]是类型表达式,不会把运行时列表自动限制为字符串。TypedDict仍是字典;需要运行时校验可用显式代码或专门的数据模型库。Protocol的结构兼容是静态概念,不等于运行时isinstance默认可用。Final、Literal和TypeAlias主要给工具阅读。
课后小问
类型提示能否阻止
average(["bad"])在运行时调用? 答案:不能,除非额外使用静态检查器或运行时校验。 解析:注解默认只是元数据;静态工具能在运行前报告不匹配,Python 解释器不会自动拦截。什么时候用
Protocol而不是基类? 答案:调用方只依赖少数成员、希望接受不同继承树的对象时。 解析:协议表达“只要具备这个接口即可”,降低了对具体继承关系的耦合。
本节小结
类型提示描述意图和接口,静态检查提前发现错误,运行时校验守住外部边界;三者职责不要混淆。
快速回顾
- 基础:变量注解、返回类型、
T | None - 结构:
TypeAlias、TypedDict - 限定:
Literal、Final - 可调用:
Callable、泛型TypeVar - 结构化类型:
Protocol、TypeGuard
阿源的知识库