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

时间:2019-02-21 07:50:34

标签: python python-3.x mypy

这是我的代码:

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 throw error: Argument 1 of "__eq__" incompatible with supertype "object"

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

1 个答案:

答案 0 :(得分:4)

根本问题是,__eq__方法应该接受任何对象:my_object == 3在运行时是合法的,并且应始终返回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将不再抱怨。从技术上讲,这应该返回布尔值,但是从实用上来说,如果您将一个person对象与一个int进行比较,您的代码可能存在一个错误。

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

请注意,您需要使用master中最新版本的mypy才能使用此标志,直到发布下一版本的mypy(0.680)。在撰写本文时,大约应在2至3周内完成。


如果由于上述原因无法以上述方式定义__eq__,我个人建议抑制类型错误,而不要用Any代替Person。

因此基本上,请执行以下操作:

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

...也许还有简短的注释说明为什么要抑制该错误。

这里的理由是严格说来__eq__的定义 是不安全的(它违反了称为Liskov替换原理的东西),如果您需要做一些不安全的事情,那就是最好显式地标记您正在颠覆类型系统,而不是使用Any来隐藏它。

至少,通过这种方式,您仍然可以使诸如Person() == 3之类的表达式成为类型错误-如果您使用Any,则诸如Person() == 3之类的表达式将自动进行类型检查。届时,您最好只使用object并构建代码以使其行为正确。