Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 273
plafer Avatar asked Jan 01 '26 10:01

plafer


2 Answers

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.

like image 143
user2357112 supports Monica Avatar answered Jan 02 '26 23:01

user2357112 supports Monica


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.

like image 33
user94559 Avatar answered Jan 03 '26 00:01

user94559



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!