从__eq__返回NotImplemented

max*_*max 7 python equality python-3.x python-internals

NotImplemented__eq__python 3中的特殊方法返回的结果是什么(如果重要,则为3.5)?

文件不清楚; 我发现唯一相关文字只是模糊地提到"其他一些后备":

NotImplemented返回时,解释器将尝试在其他类型的,或其他一些后备的反射操作,取决于运营商.如果所有尝试的操作都返回NotImplemented,则解释器将引发适当的异常.有关更多详细信息,请参阅实现算术运算.

不幸的是,"更多细节"链接根本没有提到__eq__.

我对这段摘录的解读表明,下面的代码应该引发"适当的例外",但它不会:

class A:
  def __eq__(self, other):
    return NotImplemented

class B:
  def __eq__(self, other):
    return NotImplemented

# docs seems to say these lines should raise "an appropriate exception"
# but no exception is raised
a = A()
b = B()
a == b # evaluates as unequal
a == a # evaluates as equal
Run Code Online (Sandbox Code Playgroud)

从实验开始,我认为当NotImplemented返回时__eq__,解释器的行为就好像__eq__首先没有定义一样(具体来说,它首先交换参数,如果这不能解决问题,它会使用__eq__评估的默认值进行比较)如果两个对象具有相同的身份,则"相等").如果是这种情况,我可以在文档中找到此行为的确认吗?

编辑:请参阅Python issue 28785

MSe*_*ert 13

实际上==and !=check 的工作与排序比较运算符(<和类似运算符)相同,只是它们不会引发适当的异常,而是回退到身份比较。这是唯一的区别。

这可以在CPython 源代码(版本 3.5.10)中轻松看到。我将包含该源代码的 Python 版本(至少尽可能):

_mirrored_op = {'__eq__': '__eq__',  # a == b => b == a
                '__ne__': '__ne__',  # a != b => b != a
                '__lt__': '__gt__',  # a < b  => b > a
                '__le__': '__ge__',  # a <= b => b >= a
                '__ge__': '__le__',  # a >= b => b <= a
                '__gt__': '__lt__'   # a > b  => b < a
               }

def richcmp(v, w, op):
    checked_reverse = 0
    # If the second operand is a true subclass of the first one start with
    # a reversed operation.
    if type(v) != type(w) and issubclass(type(w), type(v)) and hasattr(w, op):
        checked_reverse = 1
        res = getattr(w, _mirrored_op[op])(v)     # reversed
        if res is not NotImplemented:
            return res
    # Always try the not-reversed operation
    if hasattr(v, op):
        res = getattr(v, op)(w)      # normal
        if res is not NotImplemented:
            return res
    # If we haven't already tried the reversed operation try it now!
    if not checked_reverse and hasattr(w, op):
        res = getattr(w, _mirrored_op[op])(v)      # reversed
        if res is not NotImplemented:
            return res
    # Raise exception for ordering comparisons but use object identity in 
    # case we compare for equality or inequality
    if op == '__eq__':
        res = v is w
    elif op == '__ne__':
        res = v is not w
    else:
        raise TypeError('some error message')

    return res
Run Code Online (Sandbox Code Playgroud)

并调用a == b然后评估为richcmp(a, b, '__eq__')。这if op == '__eq__'是使您a == b返回False(因为它们不是相同的对象)和a == a返回True(因为它们是)的特殊情况。

然而,Python 2.x 中的行为完全不同。在回退到身份比较之前,您最多可以进行 4 次(甚至 6 次,我记不太清了)比较!


Eth*_*man 6

不确定它在文档中的位置(或是否),但基本行为是:

  • 尝试操作: __eq__(lhs, rhs)
  • 如果结果不是NotImplemented返回它
  • 否则尝试反射操作: __eq__(rhs, lhs)
  • 如果结果不是NotImplemented返回它
  • 否则使用适当的回退:

    eq -> 相同的对象?-> 真,否则假

    ne -> 不同的对象?真,否则假

    许多其他 -> 引发异常

其原因eqne根本没有引发异常是:

  • 他们总是可以确定的(苹果==橙色?不)

  • 在我看来,这是一个极其糟糕的设计决策。别介意它会导致难以捕获错误,但其基本原理毫无意义:当实现确定两个对象不具有可比性(“NotImplemented”)时,为什么对象标识 *every* 会产生“True”?事实并非如此,除非 `__eq__` 的实现从一开始就被破坏了。 (2认同)