为啥为 __eq__ 定义参数类型会引发 MyPy 类型错误?

Posted

技术标签:

【中文标题】为啥为 __eq__ 定义参数类型会引发 MyPy 类型错误?【英文标题】:Why does defining the argument types for __eq__ throw a MyPy type error?为什么为 __eq__ 定义参数类型会引发 MyPy 类型错误? 【发布时间】:2016-09-30 03:46:44 【问题描述】:

我正在使用 Python 3.5.1 和新发布的MyPy v0.4.1 静态类型分析器。

我有一些更复杂的代码,我已将其简化为重现错误所需的最简单的 Python 类:

class MyObject(object):
    def __init__(self, value: int=5) -> None:
        self.value = value

    def __eq__(self, other: MyObject) -> bool:
        return self.value == other.value

运行类型检查器mypy test.py 会产生以下错误:

test.py: note: In class "MyObject":
test.py:5: error: Argument 1 of "__eq__" incompatible with supertype "object"

我基于these docs 的理论是对象上的__eq____ne__ 已经定义了类型,这与我的子类对这些类型的重新定义相冲突。我的问题是如何定义这些类型以确保 __eq__ 使用我选择的类型进行类型检查。

【问题讨论】:

【参考方案1】:

== 应该采用任意其他对象,而不仅仅是您类型的对象。如果它不能识别另一个对象,它应该返回NotImplemented

class MyObject(object):
    def __init__(self, value: int=5) -> None:
        self.value = value

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, MyObject):
            return NotImplemented
        return self.value == other.value

NotImplemented 不是bool 的实例,但 mypy 似乎有一个奇怪的特殊情况。它希望返回注解为bool,并且它不会抱怨return NotImplemented 行。

另外,如果您需要参考MyObject 以获取其自身内部的类型提示,则需要使用字符串'MyObject' 而不是MyObjectMyObject 还不存在。

【讨论】:

Re: 在它自己的正文中引用MyObject,我已经在我的代码的其他地方没有引用,我没有从 MyPy 得到错误:github.com/pirate/py-data/blob/master/… @NickSweeting:你跑了吗? You should get an error when you try to run it. 啊,谢谢@user2357112,我只尝试了 mypy-ing 并忽略了实际运行它。将其更改为字符串有效。 我刚刚意识到的一件事,我以前从未见过函数返回异常类型(而不是raiseing 他们),这是标准做法吗? @NickSweeting:NotImplemented 也不例外。您正在考虑 NotImplementedError。返回 NotImplemented 是您表示您的特殊方法没有为操作指定含义的方式,因此 Python 应该尝试其他对象的方法。我听说这是一个返回值,而不是出于速度目的的异常。【参考方案2】:

您对文档的阅读是正确的 - 您需要为方法 (__eq__) 提供与基类 (object) 中已有的签名相同的签名,或者更宽松的签名。

原因是因为您的MyObjectobject 的子类型,所以MyObject 可以传递到任何需要object... 这意味着该代码可以将其与任何其他代码进行比较object,并且类型检查器没有合法的投诉方式。因此,为了反映这一点,您的__eq__ 必须写成期望任何object

你可以做的是在方法体的前面,检查类型并返回(或引发异常):

if not isinstance(other, MyObject):
  return False

那么作为those docs say,Mypy 足够聪明,在检查之后,它会知道otherMyObject 并相应地对待它。

【讨论】:

【参考方案3】:

使用“isinstance()”的测试仅在没有继承的情况下才有效,如果有,您需要在派生类中覆盖 eq 或使用 if type(self) != type(other)

【讨论】:

以上是关于为啥为 __eq__ 定义参数类型会引发 MyPy 类型错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Python 文档说我在定义 __eq__ 时需要定义 __ne__?

为啥 `if None.__eq__("a")` 似乎评估为 True(但不完全)?

设置 pyflake 和 mypy 忽略同一行

为啥 Python 有一个 __ne__ 运算符方法而不仅仅是 __eq__?

为啥类定义的关键字参数在删除后会重新出现?

mypy 无法识别从超类继承的变量