bigywater.tech
Python · 57

typing.overload:一个函数如何拥有多组类型签名

真实给一个函数声明两组@overload签名,验证mypy如何按参数类型精确匹配返回类型,以及运行时真正被执行的永远只有那份未被装饰的实现。

stringify(5)/stringify([1,2,3])
真实mypy精确匹配到不同的返回类型
返回值类型也被静态校验
真实赋给错误类型的变量会被抓出来
运行时唯一执行的函数
真实只有最后那份没被@overload装饰的实现

做的事:真实给同一个函数名声明两组签名,加一份真正的实现

代码@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会怎样

验证:mypy真实根据参数类型,精确选中对应那组签名的返回类型

实测 · 本机执行输出(mypy 2.3.1)
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出这种真实的多对多映射)。

验证:@overload标注的stub函数体真实一次都没有被执行过

实测 · 本机执行输出
stringify(5)         → 5              ← 真实结果
stringify([1,2,3])   → ['1', '2', '3']  ← 真实结果
calls recorded: ['real implementation called', 'real implementation called']   ← 真实只有2次,且都来自最后那份真正的实现
这是本文认为@overload最容易被误解的一点:stringify这个名字在源码里真实被定义了3次——两次被@overload装饰、函数体只写了...,最后一次没有@overload装饰、真实包含实际逻辑。运行时的def语句是顺序执行的,第三次def stringify(x):真实会覆盖掉前两次绑定的stringify名字——所以调用stringify(...),无论传int还是list,真实调用的都只是最后这份唯一的实现,前两个@overload版本的函数体(哪怕它们真的写了什么代码而不是...)真实永远不会被执行,因为那两个函数对象在真正的实现覆盖它们之后,就已经没有任何引用指向它们了。@overload装饰器本身在运行时真实做的事情很简单,只是把这个函数注册进一个内部注册表供类型检查工具查阅,然后原样返回这个函数(或者一个会在真的被调用时报错提示"这是个overload stub"的占位符,取决于具体实现),本质上跟TypeGuard、Protocol一样——它是一套只在静态检查阶段起作用的声明,函数真正跑起来靠的永远是那份没被@overload装饰的实现。

分步演示:从mypy按参数精确匹配到运行时唯一的真正实现

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

overload真实的静态匹配和运行时唯一真正执行的实现
这一步新验证的调用

    本文用到的工具

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