Python抽象基类abstractmethod带默认参数的typing标注问题
Python抽象基类抽象方法不限入参的typing标注方案
报错原因
mypy 抛出签名不兼容错误是因为默认执行里氏替换原则(LSP)检查:父类的learn方法声明支持任意位置参数、关键字参数,意味着所有持有MyBaseClass类型实例的代码,都可以给learn传入任意参数;但你的子类learn最多仅支持2个参数,不符合 LSP 要求,因此触发报错。
解决方案
根据你的使用场景,可选择以下两种方案:
方案1:不需要通过基类类型调用learn,仅要求子类存在返回None的learn方法
这种场景完全匹配你「不对入参做强制限制」的需求,有两种实现方式:
- 方式1:用
typing.Protocol做结构子类型检查(Python 3.8+内置,更低版本需安装typing-extensions包),不需要显式继承,只要子类存在符合要求的方法就会被识别为合法子类型:
from typing import Protocol, Any class Learnable(Protocol): def learn(self, *args: Any, **kwargs: Any) -> None: ... class MyOtherClass: def learn(self, alpha=0.0, beta=1) -> None: # 业务逻辑 return None
- 方式2:如果需要保留ABC的运行时抽象类校验能力,可单独关闭子类方法的覆写签名检查:
from abc import ABC, abstractmethod from typing import Any class MyBaseClass(ABC): @abstractmethod def learn(self, *args: Any, **kwargs: Any) -> None: raise NotImplementedError() class MyOtherClass(MyBaseClass): # 仅忽略当前方法的签名覆写检查,不影响全局mypy规则 def learn(self, alpha=0.0, beta=1) -> None: # type: ignore[override] # 业务逻辑 return None
方案2:需要保证LSP兼容,可通过基类类型安全调用learn
如果你需要用基类类型统一调用不同子类的learn方法,就要调整子类的签名兼容父类的任意参数要求:
class MyOtherClass(MyBaseClass): def learn(self, alpha=0.0, beta=1, *args, **kwargs) -> None: # 业务逻辑 return None
这种写法会保留你需要的默认参数,同时多余的传入参数会被*args, **kwargs接收,不会触发调用异常,完全符合mypy的LSP检查规则。
内容的提问来源于stack exchange,提问作者Gavello
相关产品推荐
相关产品推荐

