真实给一个函数声明两组@overload签名,验证mypy如何按参数类型精确匹配返回类型,以及运行时真正被执行的永远只有那份未被装饰的实现。
代码@overload def stringify(x: int) -> str: ... @overload def stringify(x: list[int]) -> list[str]: ... def stringify(x): # 真正的实现,没有@overload装饰 if isinstance(x, list): return [str(i) for i in x] return str(x) a = stringify(5) b = stringify([1, 2, 3]) reveal_type(a); reveal_type(b) bad: int = stringify(5) # a应该是str,赋给int会怎样
overload_test.py:14: note: Revealed type is "str" ← stringify(5),真实匹配到第一组签名 overload_test.py:15: note: Revealed type is "list[str]" ← stringify([1,2,3]),真实匹配到第二组签名 overload_test.py:17: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
真实的匹配逻辑是:mypy看到stringify(5)这次调用,会真实按声明顺序逐个尝试@overload标注的每一组签名,找到第一个参数类型匹配的(int匹配第一组),就用那组签名声明的返回类型(str)作为这次调用的真实类型;stringify([1,2,3])真实匹配第二组(list[int] -> list[str])。这跟本系列前面验证的TypeVar不一样——TypeVar是"一个签名,类型跟着参数变化但保持某种关系",overload真实是"多组完全独立的具体签名,按参数类型精确匹配对应的那一组",能表达的关系比单纯的TypeVar更灵活(比如返回类型不是简单地跟参数类型保持一致,而是int进str出、list进list出这种真实的多对多映射)。
stringify(5) → 5 ← 真实结果 stringify([1,2,3]) → ['1', '2', '3'] ← 真实结果 calls recorded: ['real implementation called', 'real implementation called'] ← 真实只有2次,且都来自最后那份真正的实现
每一步都是本文上面用真实的mypy 2.3.1和CPython跑出来的原始结果。
mypy 2.3.1(venv中安装)typing.overload