Python 3.12 支持 PEP 695 类型参数语法(如 class C[T]:),但 TypeVar 和 Generic 用法不变;新语法仅限类定义,需提前声明 TypeVar,不改变擦除式泛型本质。

Python 3.12 的 TypeVar 和 Generic 用法没变,但支持了更简洁的泛型语法
Python 3.12 并没有引入新的泛型定义机制,TypeVar 和继承 Generic 仍是标准方式。变化在于:它正式支持 PEP 695 提出的「类型参数语法」(即 class Container[T] 这种写法),替代旧式 class Container(Generic[T])。这不是语法糖升级,而是语言级支持——解析器现在原生识别方括号里的类型参数。
常见错误是以为必须改写所有代码,其实新旧写法共存;但若想用新语法,类定义必须显式声明类型参数,且不能混用两种风格。
-
T = TypeVar("T")仍需提前定义,不能直接写class Box[T]: ...而不声明T - 新语法仅适用于类定义,函数签名中仍用
def foo(x: list[T]) -> T:等传统标注 - IDE(如 PyCharm 2023.3+、Pylance)对
class C[T]:的类型推导已基本支持,但 mypy 1.8+ 才完整兼容
如何正确定义一个带约束的泛型容器类
约束(bound)决定类型参数能接受哪些具体类型,比如只允许数字。在新语法下,约束必须通过 TypeVar 定义传递,不能写在类头的方括号里。
错误示范:class NumberBox[T: int | float]: —— 这会报 SyntaxError,Python 不允许在类头方括号中写 bound。
- 正确做法:先定义
N = TypeVar("N", bound=int | float),再写class NumberBox[N]: - 若需多个类型参数,如键值容器:
K = TypeVar("K"); V = TypeVar("V"); class Map[K, V]: ... - 协变/逆变仍通过
covariant=True或contravariant=True在TypeVar中指定,与语法无关
泛型容器方法中如何引用自身类型参数
在类内部方法中,要返回当前泛型实例(比如 copy() 返回同类型),不能写 -> Container[T],因为 T 是未绑定的类型变量;必须用 Self(来自 from typing import Self)或显式使用类名加参数。
例如:
from typing import TypeVar, Self
<p>T = TypeVar("T")</p><p>class Stack[T]:
def <strong>init</strong>(self) -> None:
self._items: list[T] = []</p><pre class="brush:php;toolbar:false;">def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def copy(self) -> Self: # ✅ 推荐:Self 表示当前具体化类型
new = Stack[T]()
new._items = self._items.copy()
return new
- 用
Self比-> Stack[T]更准确:当子类继承Stack[str]时,copy()返回的是子类实例,而非原始Stack -
Self是 Python 3.11 引入、3.12 完全稳定的特性,无需额外导入(但显式from typing import Self更清晰) - 避免在方法签名中重复写
[T],比如def map[U](self, f: Callable[[T], U]) -> Stack[U]:—— 这里U是新类型变量,需另行定义
运行时是否保留泛型信息?别指望靠 isinstance 判断
Python 的泛型是「擦除式」的,无论用旧式 Generic[T] 还是新式 class C[T]:,运行时都看不到 T 的具体类型。这意味着 isinstance(stack, Stack[str]) 永远为 False,因为 Stack[str] 不是真实类型,只是类型提示构造器。
- 实际创建的实例类型永远是
Stack(未参数化),Stack[int]只在静态检查时存在 - 若需运行时区分,得手动加字段(如
self.item_type = int)或用typing.get_args(Stack[int])解析注解(但仅限于注解中显式写出的地方) - 第三方库如
typing_extensions的get_origin/get_args在 3.12 中行为不变,不要误以为新语法改变了这点
真正容易被忽略的是:新语法让类型定义更紧凑,但没解决运行时泛型的根本限制——它始终是给工具链看的,不是给解释器执行的。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











