Python 3.12 类型系统更健壮:TypedDict 支持 Required/NotRequired 显式语义、泛型类和函数支持原生语法(PEP 695)、TypeAlias 需显式注解、新增 type 语句替代冗余别名定义。

TypedDict 字段可选性必须显式标注才可靠
3.10 中 TypedDict 只能靠 Optional 或联合类型(如 int | None)模糊表达“字段可能不存在或值为 None”,mypy 无法区分 {"port": None} 和 {"port": 8080} 是否合法,也无法校验键是否被省略。
3.12 引入 Required 和 NotRequired 后,语义彻底分离:
-
host: Required[str]→ 键必须存在,且值不能为None -
timeout: NotRequired[float | None]→ 键可完全省略;若提供,则允许None或float - 所有字段都必须显式标注
Required或NotRequired,否则 mypy 报错Missing required keys
泛型类声明从繁琐到直白
3.10 必须写:
from typing import TypeVar, Generic
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, content: T) -> None:
self.content = content
3.12 支持原生类型形参语法(PEP 695),无需导入、无冗余泛型基类:
立即学习“Python免费学习笔记(深入)”;
class Box[T]:
def __init__(self, content: T) -> None:
self.content = content
关键差异:
- 3.10 的
Generic[T]是运行时存在的元类,可能干扰 IDE 推导和序列化库(如 pydantic v1) - 3.12 的
Box[int]在 AST 层直接解析,mypy 能更早、更准地捕获类型误用(例如传入Box[str]却当Box[int]用) - 函数级泛型也同理:
def first[T](items: list[T]) -> T:比def first(items: list[T]) -> T:+TypeVar更紧凑且不易漏注解
TypeAlias 必须带注解才能被检查器识别
3.10 常见写法 Vector = list[float] 或更糟的 Vector = "list[float]",前者无类型注解,mypy 视为普通变量赋值;后者字符串字面量完全绕过类型检查。
3.12 要求显式使用 TypeAlias:
from typing import TypeAlias Vector: TypeAlias = list[float] Matrix: TypeAlias = list[Vector]
不满足以下任一条件,mypy 就不认它为类型别名:
- 必须带
: TypeAlias注解(仅声明,不赋值给变量名本身) - 只能在模块顶层定义,不能在函数内、if 分支里或循环中
- 右侧必须是合法类型表达式,
Vector = [float](缺list)会直接触发语法错误,而非静默失效
type 语句替代 class-based 类型别名
3.12 新增 type 语句(PEP 695),比 TypeAlias 更轻量、更统一:
type Vector = list[float] type Response[Status] = dict[str, Status | None]
优势在于:
- 语法更接近 type alias 的本意,无额外 import
- 支持泛型参数(
Response[int]),而TypeAlias不支持参数化 - 与
class定义保持正交:需要新类型语义时用NewType,纯等价替换就用type - VS Code + Pylance 对
type的跳转和补全支持已稳定,但部分旧版 mypy(
pyproject.toml 中的 [tool.mypy] 配置,它们就只是语法而已。


















