Mypy疑难:包装重载函数时保留重载签名的类型标注问题
解决重载包装器函数的Mypy类型标注问题
我尝试给重载函数添加正确的类型标注,同时让包装器函数保留原函数的重载签名。于是给包装器也定义了重载,但调用被包装的原函数时,因为不存在匹配所有重载变体的调用方式,导致Mypy报错。请问有没有不用type: ignore[call-overload]的解决办法?
代码示例
from typing import overload @overload def update_resource(id_: str, *, data: str) -> None: ... @overload def update_resource(id_: str, *, config: str) -> None: ... def update_resource( id_: str, *, data: str | None = None, config: str | None = None ) -> None: pass @overload def wrapped_update_resources(id_: str, *, data: str) -> None: ... @overload def wrapped_update_resources(id_: str, *, config: str) -> None: ... def wrapped_update_resources( id_: str, *, data: str | None = None, config: str | None = None ) -> None: # 这里还有其他逻辑,但和当前问题无关 return update_resource(id_=id_, data=data, config=config) # type: ignore
Mypy报错信息
file.py:33: error: No overload variant of "update_resource" matches argument types "str", "Optional[str]", "Optional[str]" file.py:33: note: Possible overload variants: file.py:33: note: def update_resource(id_: str, *, data: str) -> None file.py:33: note: def update_resource(id_: str, *, config: str) -> None
解决方案
方法1:分支处理+类型断言
既然包装器的重载已经限制了外部调用只能传入data或config中的一个,内部实现里不会出现两者都为None的情况。我们可以通过分支判断明确参数的非空性,让Mypy识别到每个分支的调用都匹配对应的重载:
from typing import overload, cast # 原函数的重载和定义保持不变 def wrapped_update_resources( id_: str, *, data: str | None = None, config: str | None = None ) -> None: # 这里还有其他逻辑,但和当前问题无关 if data is not None: return update_resource(id_=id_, data=cast(str, data)) elif config is not None: return update_resource(id_=id_, config=cast(str, config)) # 兜底逻辑:因为重载不允许两者都不传,这里可以抛出异常 raise ValueError("必须传入data或config中的一个参数")
方法2:精确缩小忽略范围
如果不想修改实现逻辑,也可以把模糊的type: ignore替换为更精确的忽略指令,只屏蔽当前的重载匹配错误,避免忽略其他潜在问题:
return update_resource(id_=id_, data=data, config=config) # type: ignore[call-overload]
报错原因说明
原代码中,包装器的实现函数把data和config都定义为Optional[str],Mypy会默认认为存在两者都为None的调用场景,但原函数的重载要求必须传入其中一个非空的关键字参数,因此触发了不匹配报错。而包装器的重载已经约束了外部调用的合法输入,内部实际上不会出现非法调用,我们需要通过分支断言或精确忽略的方式让Mypy认可这一点。
内容的提问来源于stack exchange,提问作者charlemagne
相关产品推荐
相关产品推荐

