plafer
plafer

Reputation: 185

Python 3 == operator

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

Answers (3)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477824

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.

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

user2357112
user2357112

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

user94559
user94559

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

Related Questions