Reputation: 2712
The following code works with Python 2.7:
>>> class Derived(int):
... def __eq__(self, other):
... return int.__eq__(other)
...
>>> Derived(12) == 12.0
True
>>> Derived(12) == 13
False
I do not understand, why it works, given that the self
attribute is not explicitly given to int.__eq__()
method call.
[EDIT]
Answers so far suggested, that it is about returning NotImplemented
by self.__eq__(other)
and thus calling other.__eq__(self)
. Then Derived(12) == Derived(12)
I expect to be an infinitive recursion, which is not the case:
>>> Derived(12) == Derived(12)
True
Upvotes: 1
Views: 1090
Reputation: 5609
In Python 2.7, if you call int.__eq__
it always returns NotImplemented
. Example:
>>> int.__eq__(12.0)
NotImplemented
When you use the ==
operator it will attempt to run the __eq__
method on the left argument, and if it gets NotImplemented
it will return the result of the __eq__
method from the argument on the right.
In your example for Derived(12) == 12.0
, the interpreter first tries Derived(12).__eq__(12.0)
, and gets NotImplemented
. It then runs the __eq__
method on the float
number 12.0
and gets True
.
In the case of your Derived(12) == Derived(12)
example, what's likely happening is that since both objects return NotImplemented
for their __eq__
methods, and since Derived
inherits from int
, the interpreter falls back to using the cmp
builtin behavior for int
(according to this answer, which is linked-to in another answer to your question).
Here's an example that illustrates your case:
class Derived(int):
def __eq__(self, other):
print 'Doing eq'
return NotImplemented
def __cmp__(self, other):
print 'doing cmp'
return 0 # contrived example - don't do this
>>> Derived(12) == Derived(12)
doing eq
doing eq
doing cmp
True
Upvotes: 0
Reputation: 251106
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
Upvotes: 5
Reputation: 74
When mixing float with an integer type, there's no good uniform approach. https://github.com/python/cpython/blob/2.7/Objects/floatobject.c#L401-L417
P.S. How int() object using "==" operator without __eq__() method in python2?
Upvotes: 1