It works because int.__eq__(<something>)
returns NotImplemented
and when that happens it results in a call to other.__eq__(self)
and that's what is returning True
and False
here.
Demo:
class Derived(int):
def __eq__(self, other):
print self, other
print int.__eq__(other)
print other.__eq__(self)
return int.__eq__(other)
>>> Derived(12) == 12.0
12 12.0
NotImplemented
True
True
>>> Derived(12) == 13.0
12 13.0
NotImplemented
False
False
From NotImplemented
's docs:
Special value which should be returned by the binary special methods
(e.g. __eq__()
, __lt__()
, __add__()
, __rsub__()
, etc.) to
indicate that the operation is not implemented with respect to the
other type; may be returned by the in-place binary special methods
(e.g. __imul__()
, __iand__()
, etc.) for the same purpose. Its
truth value is true.
Note When NotImplemented
is returned, the interpreter will then try
the reflected operation on the other type, or some other fallback,
depending on the operator. If all attempted operations return
NotImplemented, the interpreter will raise an appropriate exception.
What happens when both __eq__
return NotImplemented
?
The behaviour is different in Python 2 and 3.
In Python 2 it falls back to __cmp__
method first and integers have __cmp__
method in Python 2. It has been removed in Python 3.
As per Python 2 docs if nothing is found it ultimately falls back to identity comparison:
If no __cmp__()
, __eq__()
or __ne__()
operation is defined, class
instances are compared by object identity (“address”)
class Derived(int):
def __eq__(self, other):
print ("Inside __eq__")
return NotImplemented
def __cmp__(self, other):
print ("Inside __cmp__ finally")
return True
>>> Derived(12) == Derived(12)
Inside __eq__
Inside __eq__
Inside __cmp__ finally
False
Not let's define a class with no method defined:
class Derived(object):
pass
>>> Derived() == Derived()
False
>>> d = Derived()
>>> d == d # Same objects.
True
Python 3 doesn't have __cmp__
method anymore but it seems to be falling back to identity now. And it seems it is not documented either.
# Python 3.5
>>> Derived() == Derived()
False
>>> d = Derived()
>>> d == d
True