根本问题在于__eq__
方法应该接受任何对象:my_object == 3
在运行时是合法的,并且应该始终返回 False。您可以通过检查基线类型定义来亲自看到这一点object
在Typeshed中 https://github.com/python/typeshed/blob/master/stdlib/2and3/builtins.pyi#L31: 的签名__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 版本,它会打印出一条注释,建议您以这种方式构建代码。
然而,这种方法的问题是,如果你做了一些愚蠢的事情,mypy 现在将不再抱怨Person() == 3
。从技术上讲,这应该返回一个 bool,但实际上,如果您将 person 对象与 int 进行比较,您的代码可能存在错误。
值得庆幸的是,mypy 最近获得了一个可以标记此类错误的功能:--strict-equality https://github.com/python/mypy/pull/6370。现在,当您使用该标志运行 mypy 时,执行以下操作Person() == 3
会使 mypy 输出错误,例如Non-overlapping equality check (left operand type: "Person", right operand type: "int")
即使你定义__eq__
按照上述方式。
请注意,您需要使用 master 提供的最新版本的 mypy 才能使用此标志,直到发布下一个版本的 mypy (0.680)。截至撰写本文时,这应该会在大约 2 到 3 周内发生。
如果定义__eq__
无论出于何种原因,您都无法以上述方式执行此操作,我个人建议抑制类型错误,而不是将 Person 替换为Any
.
所以基本上,这样做:
def __eq__(self, other: 'Person') -> bool: # type: ignore
return self.id == other.id
...也许还有一个简短的说明,说明为什么您要抑制该错误。
这里的基本原理是这个定义__eq__
严格来讲is不安全(它违反了里氏替换原则)——如果您需要做一些不安全的事情,最好明确标记您正在破坏类型系统,而不是使用 Any 来隐藏它。
至少这样,你仍然可以做出这样的表达Person() == 3
是一个类型错误——如果你使用Any
, 表达式如Person() == 3
会默默地进行类型检查。那时,您不妨使用object
并构建您的代码以使其行为正确。