Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why/When in Python does `x==y` call `y.__eq__(x)`?

The Python docs clearly state that x==y calls x.__eq__(y). However it seems that under many circumstances, the opposite is true. Where is it documented when or why this happens, and how can I work out for sure whether my object's __cmp__ or __eq__ methods are going to get called.

Edit: Just to clarify, I know that __eq__ is called in preferecne to __cmp__, but I'm not clear why y.__eq__(x) is called in preference to x.__eq__(y), when the latter is what the docs state will happen.

>>> class TestCmp(object): ...     def __cmp__(self, other): ...         print "__cmp__ got called" ...         return 0 ...  >>> class TestEq(object): ...     def __eq__(self, other): ...         print "__eq__ got called" ...         return True ...  >>> tc = TestCmp() >>> te = TestEq() >>>  >>> 1 == tc __cmp__ got called True >>> tc == 1 __cmp__ got called True >>>  >>> 1 == te __eq__ got called True >>> te == 1 __eq__ got called True >>>  >>> class TestStrCmp(str): ...     def __new__(cls, value): ...         return str.__new__(cls, value) ...      ...     def __cmp__(self, other): ...         print "__cmp__ got called" ...         return 0 ...  >>> class TestStrEq(str): ...     def __new__(cls, value): ...         return str.__new__(cls, value) ...      ...     def __eq__(self, other): ...         print "__eq__ got called" ...         return True ...  >>> tsc = TestStrCmp("a") >>> tse = TestStrEq("a") >>>  >>> "b" == tsc False >>> tsc == "b" False >>>  >>> "b" == tse __eq__ got called True >>> tse == "b" __eq__ got called True 

Edit: From Mark Dickinson's answer and comment it would appear that:

  1. Rich comparison overrides __cmp__
  2. __eq__ is it's own __rop__ to it's __op__ (and similar for __lt__, __ge__, etc)
  3. If the left object is a builtin or new-style class, and the right is a subclass of it, the right object's __rop__ is tried before the left object's __op__

This explains the behaviour in theTestStrCmp examples. TestStrCmp is a subclass of str but doesn't implement its own __eq__ so the __eq__ of str takes precedence in both cases (ie tsc == "b" calls b.__eq__(tsc) as an __rop__ because of rule 1).

In the TestStrEq examples, tse.__eq__ is called in both instances because TestStrEq is a subclass of str and so it is called in preference.

In the TestEq examples, TestEq implements __eq__ and int doesn't so __eq__ gets called both times (rule 1).

But I still don't understand the very first example with TestCmp. tc is not a subclass on int so AFAICT 1.__cmp__(tc) should be called, but isn't.

like image 751
Singletoned Avatar asked Feb 17 '10 14:02

Singletoned


People also ask

What is __ eq __ method in Python?

Summary. Implement the Python __eq__ method to define the equality logic for comparing two objects using the equal operator ( == )

Is there a difference between == and is?

The '==' is known as the equality operator. The 'is' is known as the identity operator. The == operator helps us compare the equality of objects. The is operator helps us check whether different variables point towards a similar object in the memory.

What is __ contains __ in Python?

Python string __contains__() is an instance method and returns boolean value True or False depending on whether the string object contains the specified string object or not. Note that the Python string contains() method is case sensitive.


1 Answers

You're missing a key exception to the usual behaviour: when the right-hand operand is an instance of a subclass of the class of the left-hand operand, the special method for the right-hand operand is called first.

See the documentation at:

http://docs.python.org/reference/datamodel.html#coercion-rules

and in particular, the following two paragraphs:

For objects x and y, first x.__op__(y) is tried. If this is not implemented or returns NotImplemented, y.__rop__(x) is tried. If this is also not implemented or returns NotImplemented, a TypeError exception is raised. But see the following exception:

Exception to the previous item: if the left operand is an instance of a built-in type or a new-style class, and the right operand is an instance of a proper subclass of that type or class and overrides the base’s __rop__() method, the right operand’s __rop__() method is tried before the left operand’s __op__() method.

like image 116
Mark Dickinson Avatar answered Oct 19 '22 07:10

Mark Dickinson