python泛型依赖type hints与mypy等工具实现静态类型检查,核心是generic、typevar和protocol;typevar定义类型变量并可加约束或协变性,generic用于参数化类,protocol支持结构化接口,泛型信息运行时被擦除。

Python 的泛型不是编译时强约束,而是靠类型提示(Type Hints)+ 类型检查工具(如 mypy)协同实现的。写泛型代码的核心是用 Generic、TypeVar 和 Protocol 表达“类型参数化”的意图,让函数或类能安全地处理多种类型,同时保留类型信息供 IDE 提示和静态检查。
用 TypeVar 定义可复用的类型变量
TypeVar 是泛型的起点,它声明一个占位符类型,后续可在函数签名、类定义中反复引用。关键点是:类型变量需显式绑定约束或协变性,否则默认是 invariant(不变型),容易导致误报。
- 基础用法:
T = TypeVar('T')表示任意类型;def first(lst: List[T]) -> T:能推断返回值与列表元素同类型 - 加约束更安全:
Number = TypeVar('Number', int, float),限制 T 只能是 int 或 float,避免传入 str 导致运行时错误 - 协变/逆变需明确:
class Container(Generic[T_co], covariant=True)(需继承Generic并设bound或covariant参数)
在类中使用 Generic 实现参数化容器
自定义泛型类必须继承 Generic[...],并在类名后标注类型参数。注意:运行时不生效,仅用于类型检查和 IDE 支持。
- 正确写法:
class Stack(Generic[T]): def push(self, item: T) -> None: ... def pop(self) -> T: ... - 不能只写
class Stack[T]:(这是 Python 3.12+ 的简写语法,但需确保环境支持且 mypy 版本兼容) - 若类有多个类型参数,如
class Pair(Generic[K, V]):,需一一对应使用,不可省略或错序
用 Protocol 替代继承,实现结构化泛型接口
当不想强制用户继承某个基类,又希望约束“具备某些方法”,就用 Protocol。它是鸭子类型 + 类型安全的结合,比抽象基类(ABC)更轻量、更灵活。
立即学习“Python免费学习笔记(深入)”;
- 定义协议:
class Drawable(Protocol): def draw(self) -> None: ... - 泛型函数接受协议:
def render_all(items: Sequence[Drawable]) -> None:,任何有draw()方法的对象都可通过检查 - 可与 TypeVar 结合:
T_draw = TypeVar('T_draw', bound=Drawable),进一步限定泛型参数必须满足协议
避免常见陷阱:运行时擦除与检查时机
Python 泛型在运行时被完全擦除(type parameter 消失),所以 isinstance(x, Stack[int]) 会报错。所有类型逻辑只在开发阶段起作用。
- 不要试图在运行时获取泛型参数:
Stack[int].__args__在 3.8+ 可读,但不保证稳定,也不该用于逻辑分支 - 单元测试仍需覆盖真实数据类型,类型提示不能替代测试
- mypy 默认不检查未标注函数,记得对关键模块启用
--disallow-untyped-defs








