mypy:“__eq__”与超类型 "object" 不兼容

mypy: "__eq__" incompatible with supertype "object"

这是我的代码:

class Person:
    def __init__(self, id):
        self.id = id

    def __eq__(self, other: 'Person') -> bool:
        return self.id == other.id

   def compare(self, other: 'Person') -> bool:
        return self.id == other.id

mypy 抛出 error: Argument 1 of "__eq__" incompatible with supertype "object"

但是如果我删除 __eq__ 方法,虽然 compare__eq__ 相同,但 mypy 不会抱怨它,我该怎么办?

根本问题是 __eq__ 方法应该接受任何对象:my_object == 3 在 运行 时是合法的,并且应该总是 return False。您可以通过检查 object in Typeshed 的基线类型定义来亲眼看到:__eq__ 的签名为 def __eq__(self, o: object) -> bool: ...

因此,为了完成这项工作,实施 __eq__ 的正确方法是执行以下操作:

def __eq__(self, other: object) -> bool:
    if not isinstance(other, Person):
        # If we return NotImplemented, Python will automatically try
        # running other.__eq__(self), in case 'other' knows what to do with
        # Person objects.
        return NotImplemented
    return self.id == other.id

事实上,如果您更新正在使用的 mypy 版本,它会打印出一条注释,建议您以这种方式构建代码。

然而,这种方法的问题是,如果你做了像 Person() == 3 这样愚蠢的事情,mypy 现在将不再抱怨。从技术上讲,这应该是 return 布尔值,但实际上,如果您将 person 对象与 int 进行比较,则您的代码可能存在错误。

值得庆幸的是,mypy 最近获得了可以标记此类错误的功能:--strict-equality。现在,当您使用该标志 运行 mypy 时,即使您以上述方式定义 __eq__,执行 Person() == 3 也会使 mypy 输出错误,例如 Non-overlapping equality check (left operand type: "Person", right operand type: "int")

请注意,在下一个版本的 mypy (0.680) 发布之前,您需要使用 master 的最新版本的 mypy 才能使用此标志。在撰写本文时,这应该会在大约 2 到 3 周内发生。


如果以上述方式定义 __eq__ 无论出于何种原因您都无法做到,我个人建议抑制类型错误,而不是将 Person 替换为 Any.

所以基本上,这样做:

def __eq__(self, other: 'Person') -> bool:  # type: ignore
    return self.id == other.id

...可能会附上一条简短的说明,说明您为什么要抑制错误。

这里的基本原理是 __eq__ 的这个定义严格来说 不安全的(它违反了被称为 Liskov 替换原则的东西)——如果你需要做一些不安全的事情,最好明确标记你正在颠覆类型系统,而不是使用 Any 隐藏它。

至少这样,您仍然可以使 Person() == 3 等表达式成为类型错误——如果您使用 AnyPerson() == 3 等表达式将自动进行类型检查。到那时,您不妨只使用 object 并构建您的代码以使其正常运行。