带参/无参可选staticmethod的类型标注兼容问题排查
解决子类用staticmethod实现基类可选Callable属性时的mypy类型不兼容问题
我尝试为基类中初始值为None的方法属性添加类型标注,允许子类通过@staticmethod实现该方法。但带参数的实现会触发mypy的类型不兼容错误,无参数的情况却能正常通过检查。
报错代码示例
from typing import ClassVar, Optional, Callable class BaseClass: attr: ClassVar[Optional[Callable[[str], None]]] = None class ChildClass1(BaseClass): pass class ChildClass2(BaseClass): @staticmethod def attr(s: str): return "hello"
mypy抛出的错误:
test.py:14: error: Signature of "attr" incompatible with supertype "BaseClass" [override]
无参数的正常代码示例
from typing import ClassVar, Optional, Callable class BaseClass: attr: ClassVar[Optional[Callable[[], None]]] = None class ChildClass2(BaseClass): @staticmethod def attr(): return "hello"
问题原因
mypy在检查类属性覆盖时会做严格的类型对比:基类里attr标注的是Optional[Callable[[str], None]],但子类用@staticmethod定义的attr本质是staticmethod对象,而非直接的Callable类型。无参数场景下碰巧兼容是因为staticmethod的调用签名和基类的无参Callable隐式匹配,但带参数时mypy会校验类型归属,发现两者不是直接兼容的类型,因此抛出错误。
解决方案
方案1:基类标注兼容staticmethod类型
修改基类的类型标注,让attr同时支持Callable和staticmethod类型:
from typing import ClassVar, Optional, Callable, Union class BaseClass: attr: ClassVar[Optional[Union[Callable[[str], None], staticmethod]]] = None class ChildClass2(BaseClass): @staticmethod def attr(s: str): return "hello"
方案2:用Protocol定义兼容的调用签名
通过typing.Protocol定义匹配目标调用签名的协议,任何满足该签名的可调用对象(包括staticmethod)都会被认可:
from typing import ClassVar, Optional, Protocol class AttrProtocol(Protocol): def __call__(self, s: str) -> None: ... class BaseClass: attr: ClassVar[Optional[AttrProtocol]] = None class ChildClass2(BaseClass): @staticmethod def attr(s: str): return "hello"
方案3:改用抽象基类(更符合OOP规范)
如果业务场景允许,用抽象基类定义抽象方法,让子类实现,这种方式更符合Python面向对象设计,mypy也能完美校验:
from abc import ABC, abstractmethod class BaseClass(ABC): @staticmethod @abstractmethod def attr(s: str) -> None: pass class ChildClass2(BaseClass): @staticmethod def attr(s: str): return "hello"
内容的提问来源于stack exchange,提问作者Ivailo Karamanolev
相关产品推荐
相关产品推荐

