这是我的代码:
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__
方法,mypy不会抱怨它虽然compare
与__eq__
相同,我该怎么办?
根本问题是__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现在不再抱怨了。从技术上讲,这应该返回一个bool,但实际上,如果你将一个person对象与一个int进行比较,你的代码可能会有一个bug。
值得庆幸的是,mypy最近获得了一个可以标记这些错误的功能:--strict-equality
。现在,当您使用该标志运行mypy时,即使您按照上述方式定义Person() == 3
,执行Non-overlapping equality check (left operand type: "Person", right operand type: "int")
也会使qpyxswpoi出现mypy输出错误。
请注意,您需要使用最新版本的mypy来使用此标志,直到mypy(0.680)的下一个版本发布。这应该在写作时间大约2至3周内发生。
如果以上述方式定义__eq__
不是出于任何原因你可以做的事情,我个人建议抑制类型错误而不是用__eq__
替换Person。
所以基本上,这样做:
Any
...也许还有一个简短的说明,说明你为什么要压制这个错误。
这里的基本原理是严格来说这个def __eq__(self, other: 'Person') -> bool: # type: ignore
return self.id == other.id
的定义是不安全的(它违反了被称为Liskov替换原则的东西) - 如果你需要做一些不安全的事情,那么明确标记你是在颠覆类型系统可能更好然后使用Any隐藏它。
至少这样,你仍然可以使像__eq__
这样的表达式成为类型错误 - 如果你使用Person() == 3
,像Any
这样的表达式会默默地进行类型检查。此时,您可以使用Person() == 3
并构建代码以正确运行。