You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:56:00