NotImplemented return from __eq__

What is the result of returning NotImplemented from the __eq__ special method in python 3 (fine 3.5, if that matters)?

The documentation is fuzzy; only the relevant text that I found only vaguely refers to "some other fallback":

When NotImplemented returned, the interpreter will then try the reflected operation on another type or some other backup, depending on the statement. If all attempted operations are returned NotImplemented , the interpreter will raise a corresponding exception. For more information, see Implementing Arithmetic Operations .

Unfortunately, the "Details" link does not mention __eq__ at all.

My reading of this excerpt suggests that the code below should raise a “corresponding exception”, but that is not the case:

 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 

From an experiment, I think that when NotImplemented returns from __eq__ , the interpreter behaves as if __eq__ not defined in the first place (in particular, it swaps the arguments first, and if that doesn't solve the problem, it compares using standard __eq__ , which evaluates to "equal" if both objects have the same identifier). If this is the case, where in the documentation can you find confirmation of this behavior?

Edit: see Python issue 28785

+6
source share
2 answers

Not sure where (or if) it is in the docs, but the main behavior is:

  • try the operation: __eq__(lhs, rhs)
  • if the result is not NotImplemented returns it
  • try the reflected operation: __eq__(rhs, lhs)
  • if the result is not NotImplemented returns it
  • otherwise use the appropriate digression:

    eq → same objects? → True, else False

    ne → different objects? True, otherwise False

    many others -> exception exception

The reason eq and ne does not raise an exception:

  • they can always be defined (apple == orange? no)
+1
source

Actually checking the == and != Checks is identical to the order comparison operations ( < and the like), except that they do not raise the corresponding exception , but return to the comparison identifier. This is the only difference.

This is easy to see in the source code of CPython . I will include a version of this source code in Python (at least as much as possible):

 _mirrored_op = {'__eq__': '__eq__', # a == b => b == a '__ne__': '__ne__', # a != b => b != a '__lt__': '__ge__', # a < b => b >= a '__le__': '__gt__', # a <= b => b > a '__ge__': '__lt__', # a >= b => b < a '__gt__': '__le__' # 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('sth') return res 

and calling a == b then evaluated as richcmp(a, b, '__eq__') . if op == '__eq__' is a special case when your a == b returns False (because they are not identical objects) and your a == a return True (because they are).

However, the behavior in Python 2.x was completely different. You can have up to 4 (or even 6, I don’t remember exactly) comparisons before returning to matching identities!

+1
source

Source: https://habr.com/ru/post/1012617/


All Articles