Reputation: 185
I'm confused as to how the ==
operator works in Python 3. From the docs, eq(a, b)
is equivalent to a == b
. Also eq
and __eq__
are equivalent.
Take the following example:
class Potato:
def __eq__(self, other):
print("In Potato's __eq__")
return True
>> p = Potato()
>> p == "hello"
In Potato's __eq__ # As expected, p.__eq__("hello") is called
True
>> "hello" == p
In Potato's __eq__ # Hmm, I expected this to be false because
True # this should call "hello".__eq__(p)
>> "hello".__eq__(p)
NotImplemented # Not implemented? How does == work for strings then?
AFAIK, the docs only talk about the ==
-> __eq__
mapping, but don't say anything about what happens either one of the arguments is not an object (e.g. 1 == p
), or when the first object's __eq__
is NotImplemented
, like we saw with "hello".__eq(p)
.
I'm looking for the general algorithm that is employed for equality... Most, if not all other SO answers, refer to Python 2's coercion rules, which don't apply anymore in Python 3.
Upvotes: 2
Views: 2205
Reputation: 477824
I'm confused as to how the == operator works in Python 3. From the docs,
eq(a, b)
is equivalent toa == b
. Alsoeq
and__eq__
are equivalent.
No that is only the case in the operator
module. The operator module is used to pass an ==
as a function for instance. But operator
has not much to do with vanilla Python itself.
AFAIK, the docs only talk about the == -> eq mapping, but don't say anything about what happens either one of the arguments is not an object (e.g. 1 == p), or when the first object's.
In Python everything is an object: an int
is an object, a "class" is an object", a None
is an object, etc. We can for instance get the __eq__
of 0
:
>>> (0).__eq__
<method-wrapper '__eq__' of int object at 0x55a81fd3a480>
So the equality is implemented in the "int
class". As specified in the documentation on the datamodel __eq__
can return several values: True
, False
but any other object (for which the truthiness will be calculated). If on the other hand NotImplemented
is returned, Python will fallback and call the __eq__
object on the object on the other side of the equation.
Upvotes: 1
Reputation: 282043
You're mixing up the functions in the operator
module and the methods used to implement those operators. operator.eq(a, b)
is equivalent to a == b
or operator.__eq__(a, b)
, but not to a.__eq__(b)
.
In terms of the __eq__
method, ==
and operator.eq
work as follows:
def eq(a, b):
if type(a) is not type(b) and issubclass(type(b), type(a)):
# Give type(b) priority
a, b = b, a
result = a.__eq__(b)
if result is NotImplemented:
result = b.__eq__(a)
if result is NotImplemented:
result = a is b
return result
with the caveat that the real code performs method lookup for __eq__
in a way that bypasses instance dicts and custom __getattribute__
/__getattr__
methods.
Upvotes: 7
Reputation: 60153
When you do this:
"hello" == potato
Python first calls "hello".__eq__(potato)
. That return NotImplemented
, so Python tries it the other way: potato.__eq__("hello")
.
Returning NotImplemented
doesn't mean there's no implementation of .__eq__
on that object. It means that the implementation didn't know how to compare to the value that was passed in. From https://docs.python.org/3/library/constants.html#NotImplemented:
Note When a binary (or in-place) method returns NotImplemented the interpreter will try the reflected operation on the other type (or some other fallback, depending on the operator). If all attempts return NotImplemented, the interpreter will raise an appropriate exception. Incorrectly returning NotImplemented will result in a misleading error message or the NotImplemented value being returned to Python code. See Implementing the arithmetic operations for examples.
Upvotes: 3