I'm taking my first computing science course, and we just learned about class implementation and inheritance. In particular, we just covered method overriding and how classes we define inherit from the object
superclass by default. As one of my examples trying out this particular case of inheritance, I used the following code:
class A:
def __init__(self, i):
self.i = i
def __str__(self):
return "A"
# Commenting out these two lines to not override __eq__(), just use the
# default from our superclass, object
#def __eq__(self, other):
#return self.i == other.i
x = A(2)
y = A(2)
>>>print(x == y)
False
>>>print(x.__eq__(y))
NotImplemented
I expected the result from (x == y)
, because as I understand it the default for __eq__()
is to check if they're the same objects or not, not worrying about the contents. Which is False
, x
and y
have the same contents but are different objects. The second one surprised me though.
So my questions: I thought (x==y)
and x.__eq__(y)
were synonymous and made exactly the same call. Why do these produce differing output? And why does the second conditional return NotImplemented
?
The NotImplemented
value you're seeing returned from your inherited __eq__
method is a special builtin value used as a sentinel in Python. It can be returned by __magic__
methods that implement mathematical or comparison operators to indicate that the class does not support the operator that was attempted (with the provided arguments).
This can be more useful than raising an exception, as it allows Python to fall back to other options to resolve the operator use. For instance, if you do x + y
, Python will first try to run x.__add__(y)
. If that returns NotImplemented
, it will next try the "reverse" version, y.__radd__(x)
, which may work if y
is a more sophisticated type than x
is.
In the case you're asking about, x == y
, Python first tries x.__eq__(y)
, then y.__eq__(x)
, and finally x is y
(which will always evaluate to a Boolean value). Since object.__eq__
returns NotImplemented
in all cases, your class falls back to the identity comparison when you use the real operator, but shows you the NotImplemented
sentinel when you call __eq__
directly.