使用@singledispatchmethod装饰__eq__时mypy签名兼容错误的解决办法
解决
__eq__方法使用@singledispatchmethod时的mypy签名不兼容问题 问题根源
mypy认为被@singledispatchmethod装饰后的__eq__方法与父类object的__eq__签名不兼容——object.__eq__的签名是def __eq__(self, object, /) -> bool(含位置-only参数标记/),而装饰器会改变方法的类型标注,导致类型检查报错。
解决方法
方法1:类型强制转换(保留singledispatchmethod)
通过typing.cast告诉mypy,装饰后的方法符合父类的签名要求:
from functools import singledispatchmethod from typing import cast, Callable class D: value: int def __init__(self, value: int) -> None: self.value = value @property def foo(self) -> int: return 42 * self.value class C: value: int def __init__(self, value: int) -> None: self.value = value @singledispatchmethod def __eq__(self, other: object) -> bool: return NotImplemented @__eq__.register def _(self, other: "C") -> bool: return self.value == other.value @__eq__.register def _(self, other: D) -> bool: return self.value == other.foo / 2 # 强制转换类型,让mypy认可签名兼容 C.__eq__ = cast(Callable[[C, object], bool], C.__eq__)
优点:保留了singledispatchmethod的扩展性,后续新增类型只需添加@__eq__.register装饰的方法;缺点:需要额外的类型转换代码。
方法2:手动实现类型分发(绕过装饰器)
直接在__eq__中通过isinstance判断类型,手动实现多态逻辑:
class D: value: int def __init__(self, value: int) -> None: self.value = value @property def foo(self) -> int: return 42 * self.value class C: value: int def __init__(self, value: int) -> None: self.value = value def __eq__(self, other: object) -> bool: if isinstance(other, C): return self.value == other.value elif isinstance(other, D): return self.value == other.foo / 2 return NotImplemented
优点:代码简洁直观,完全符合mypy的类型检查要求,无需额外处理;缺点:类型数量较多时,代码会变得冗长,扩展性不如singledispatchmethod。
方法3:匹配父类位置-only签名(结合@override)
调整__eq__的参数为位置-only(添加/),并使用@override标记重写父类方法,让mypy认可签名兼容性:
from functools import singledispatchmethod from typing import override class D: value: int def __init__(self, value: int) -> None: self.value = value @property def foo(self) -> int: return 42 * self.value class C: value: int def __init__(self, value: int) -> None: self.value = value @singledispatchmethod @override def __eq__(self, other: object, /) -> bool: return NotImplemented @__eq__.register def _(self, other: "C") -> bool: return self.value == other.value @__eq__.register def _(self, other: D) -> bool: return self.value == other.foo / 2
优点:严格遵循父类方法签名,无需类型转换;缺点:依赖Python 3.12+的typing.override(旧版本需安装typing-extensions),且位置-only参数限制了参数传递方式(不过__eq__本身仅按位置传参)。
内容的提问来源于stack exchange,提问作者InSync
相关产品推荐
相关产品推荐

