如何配置mypy兼容接口方法的**kwargs可选与返回类型窄化?
问题描述
我定义了一个作为接口的类MyInterface,仅保留一个抽象方法简化展示:
import abc from typing import Union class MyInterface(metaclass=abc.ABCMeta): @classmethod def __subclasshook__(cls, subclass): return (hasattr(subclass, "do_stuff") and callable(subclass.do_stuff)) @abc.abstractmethod def do_stuff(self, input: str, output: str, **kwargs) -> Union[str, list[str]]: raise NotImplementedError
我希望子类继承该接口时,支持以下四种合法实现:
- 保留
**kwargs并返回str - 保留
**kwargs并返回list[str] - 省略
**kwargs并返回str - 省略
**kwargs并返回list[str]
对应的子类代码示例:
class ImpClass1(MyInterface): def do_stuff(self, input: str, output: str, **kwargs) -> str: ... class ImpClass2(MyInterface): def do_stuff(self, input: str, output: str, **kwargs) -> list[str]: ... class ImpClass3(MyInterface): def do_stuff(self, input: str, output: str) -> str: ... class ImpClass4(MyInterface): def do_stuff(self, input: str, output: str) -> list[str]: ...
但mypy对省略**kwargs的子类(如ImpClass3)报签名不兼容错误:
src/tests/parser/test_interface.py:43: error: Signature of "do_stuff" incompatible with supertype "MyInterface" [override] src/tests/parser/test_interface.py:43: note: Superclass: src/tests/parser/test_interface.py:43: note: def do_stuff(self, input: str, output: str, **kwargs: Any) -> Union[str, list[str]] src/tests/parser/test_interface.py:43: note: Subclass: src/tests/parser/test_interface.py:43: note: def do_stuff(self, input: str, output: str) -> str
解决方法
方法一:改用typing.Protocol定义接口
Protocol的结构子类型检查更灵活,允许子类省略**kwargs(只要核心参数匹配),同时原生支持协变返回类型(返回更具体的str或list[str])。修改接口定义如下:
from typing import Protocol, Union class MyInterface(Protocol): def do_stuff(self, input: str, output: str, **kwargs) -> Union[str, list[str]]: ...
如果需要保留运行时类型检查能力,可添加@runtime_checkable装饰器:
from typing import Protocol, Union, runtime_checkable @runtime_checkable class MyInterface(Protocol): def do_stuff(self, input: str, output: str, **kwargs) -> Union[str, list[str]]: ...
修改后mypy会自动允许你列出的四种子类实现,无需额外配置。
方法二:调整mypy配置(不推荐)
如果必须使用ABC而非Protocol,可以修改mypy配置文件(如mypy.ini或pyproject.toml),关闭方法重写的签名严格检查:
mypy.ini:
[mypy] override_signatures = false
pyproject.toml:
[tool.mypy] override_signatures = false
注意:该选项会关闭所有方法重写的签名兼容性检查,可能放过其他非法的签名破坏,仅在无法修改接口定义时使用。
补充说明
关于返回类型:mypy默认支持协变返回类型,子类返回str或list[str]代替父类的Union[str, list[str]]是合法的,无需额外配置。
内容的提问来源于stack exchange,提问作者A Simple Programmer
相关产品推荐
相关产品推荐

