这是我的代码:
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不会抱怨它,该怎么办?
答案 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
并构建代码以使其行为正确。