bigywater.tech
Python · 51

TypeVar:泛型函数如何保持类型关系

真实用mypy检查一个带TypeVar的泛型函数,对比静态推断结果和Python运行时对类型标注完全无视的真实差异。

同类型调用
真实mypy推断T为具体类型(int/str)
混合类型调用
真实退化成公共父类object,不是报错
运行时
真实Python解释器完全不检查类型标注

做的事:用同一个泛型函数,真实对比mypy静态检查和Python运行时

代码 typevar_test.pyfrom typing import TypeVar T = TypeVar("T") def pick_first(a: T, b: T) -> T: return a x = pick_first(1, 2) # 同类型调用 y = pick_first("a", "b") # 同类型调用 z = pick_first(1, "a") # 混合类型调用,会怎样? reveal_type(x); reveal_type(y); reveal_type(z) bad: int = pick_first("hello", "world") # str结果塞进int变量

验证:mypy真实用reveal_type告诉我们T被推断成了什么

实测 · 本机执行输出(mypy 2.3.1)
typevar_test.py:12: note: Revealed type is "int"       ← x,两个int参数,T=int
typevar_test.py:13: note: Revealed type is "str"        ← y,两个str参数,T=str
typevar_test.py:14: note: Revealed type is "object"      ← z,一个int一个str,真实退化成两者的公共父类
typevar_test.py:16: error: Incompatible types in assignment (expression has type "str", variable has type "int")  [assignment]
Found 1 error in 1 file

这组真实结果展示了TypeVar最核心的价值:同一个T在函数签名里出现了3次(a: T, b: T -> T),mypy真实会记住"这次调用里T到底具体是什么",并且要求同一次调用里所有用到T的地方保持一致——pick_first(1, 2)两个参数真实都是int,mypy推断这次调用T=int,返回值类型也真实跟着是int。pick_first(1, "a")这次真实没有报错,mypy的处理方式是把T推断成int和str的最近公共父类object——这是TypeVar没有约束(bound)时的默认行为,它不会强制要求两个参数类型必须相同,只是老老实实推断出一个两者都满足的类型,但因为object几乎不支持任何具体操作,这个返回值在后续代码里能做的事真实会被静态检查限制得很窄。真正被真实抓出来的错误,是最后一行:pick_first("hello", "world")推断出T=str,返回值真实是str,但被赋值给标注成int的变量bad,这才是mypy真实报的Incompatible types in assignment。

验证:Python解释器运行同一份代码,真实完全不检查这些

实测 · 本机执行输出
runtime happily ran pick_first(1, 'a'): 1 
runtime happily assigned a str into a variable annotated int: hello 
真实运行这份代码,Python解释器对TypeVar和类型标注真实是完全视而不见的——pick_first(1, "a")这种混合类型调用照样跑,bad: int = pick_first(...)把一个真实的字符串对象赋给一个标注成int的变量也完全没有任何警告或异常。这跟本系列前面验证过的"dataclass类型标注不强制生效"是同一条更大的规则:Python的类型标注体系(typing模块,包括TypeVar)从设计上就真实是纯静态、可选的——它给mypy这类工具提供了足够的信息去做编译期(严格说是检查期)推导和校验,但真实完全不介入运行时的任何一步执行。TypeVar的价值因此真实体现在两个开发阶段:写代码时IDE能提示"这个位置的类型应该是什么",跑CI时mypy能真实拦下"混用了不该混用的类型",但一旦代码通过检查进入生产环境实际运行,这套类型系统的保护就真实完全不再存在,运行时该出的bug(比如真的传错了类型的参数)不会因为写了类型标注就自动消失。

分步演示:从mypy的reveal_type到运行时的完全无视

每一步都是本文上面用真实的mypy 2.3.1和CPython跑出来的原始结果。

TypeVar在mypy和Python运行时下真实完全不同的两套行为
这一步新验证的检查阶段

    本文用到的工具

    • 本机 Python 3.14.7,真实的 mypy 2.3.1(venv中安装)
    • 标准库 typing.TypeVar
    京ICP备2026067502号-1京公网安备11010502063487号