Skip to content

类型提示与静态检查 ​

学习目标 ​

  • 为变量、函数和集合写 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.0

Python 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)
# 输出:True

T | 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"])
# 输出:Ada

TypedDict 描述字典的键结构,运行时仍然是普通 dict,不会自动检查字段。

Literal ​

python
from typing import Literal


def set_mode(mode: Literal["safe", "fast"]) -> str:
    return mode


print(set_mode("safe"))
# 输出:safe

Literal 适合有限字符串或整数选项,静态检查器可以发现拼写错误。

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)
# 输出:3

Final 提醒静态检查器禁止重新绑定,不会在运行时阻止赋值。

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 主要给工具阅读。

课后小问 ​

  1. 类型提示能否阻止 average(["bad"]) 在运行时调用? 答案:不能,除非额外使用静态检查器或运行时校验。 解析:注解默认只是元数据;静态工具能在运行前报告不匹配,Python 解释器不会自动拦截。

  2. 什么时候用 Protocol 而不是基类? 答案:调用方只依赖少数成员、希望接受不同继承树的对象时。 解析:协议表达“只要具备这个接口即可”,降低了对具体继承关系的耦合。

本节小结 ​

类型提示描述意图和接口,静态检查提前发现错误,运行时校验守住外部边界;三者职责不要混淆。

快速回顾 ​

  • 基础:变量注解、返回类型、T | None
  • 结构:TypeAlias、TypedDict
  • 限定:Literal、Final
  • 可调用:Callable、泛型 TypeVar
  • 结构化类型:Protocol、TypeGuard