1

How does Python 3 compare a built-in object (on the lhs) to a user-defined object (on the rhs)?

Does the built-in __eq__ method simply delegate the comparison to the rhs (rhs.__eq__(self))?

I didn't find any statement about this in the docs. The docs state:

Objects of different types, except different numeric types, never compare equal.

It's quite misleading because:

class X:
  def __eq__(self, rhs)
    return True

x = X()
'abc' == x # True

I think the doc statement should be rephrased as follows:

Objects of different built-in types, except different numeric types, never compare equal.

and should furthermore clarify how the comparison to user-defined class instances is performed.

max
  • 49,282
  • 56
  • 208
  • 355
  • I take your point about the wording, but to be fair, the section is called "Built-in Types", and the first sentence is "The following sections describe the standard types that are built into the interpreter". Other sentences, like "Any object can be tested for truth value", also assume that you haven't defined `def __bool__(self): raise Exception` or something. – DSM Sep 12 '12 at 00:55
  • @DSM Yeah, I realized the same on second reading. Thing is, many people read just short excerpts from the docs; the amount of context this passage requires for proper interpretation is quite beyond my normal attention range! – max Sep 12 '12 at 02:37

1 Answers1

3

To answer the questions:

How does Python 3 compare a built-in object (on the lhs) to a user-defined object (on the rhs)?

The same way as with any other object comparisons (including None!).

Does the built-in __eq__ method simply delegate the comparison to the rhs (rhs.__eq__(self))?

No. The built-in __eq__ does not delegate like this. There is a higher-construct at work that covers the behavior of == in Python.

Given a == b, where a.__eq__(b) returns NotImplemented then b.__eq__(a) will be invoked and the result of the used as the result of the equality test. (False is returned if both __eq__ implementations return NotImplemented.)

Thus, given x (of class X), and given that "abc".__eq__(x) returns NotImplemented, then x.__eq__("abc") is invoked (and evaluates to True per the question).

The same applies to the other standard comparison operators.

While I don't care to speculate too much on the documentation (or possible mis-wording), I believe it is entirely accurate if taken in context of stdObj.__eq__(obj) as opposed to stdObj == obj.

See also:

Community
  • 1
  • 1
  • Thanks.. A minor point, but I think delegating (i.e., returning `rhs.__eq__(self)`) would have the same effect as returning NotImplemented, no? – max Sep 12 '12 at 01:18
  • @max I was just thinking about that on the way home. While perhaps being semantically equivalent in this particular case, it would not work where the lhs was a custom type that did not have such a delegating-`__eq__` implementation and it *could* lead to cases of infinite recursion if not written in a very precise manner -- both `(1).__eq__("x")` and `"x".__eq__(1)` evaluate to `NotImplemented`, for instance. –  Sep 12 '12 at 01:56
  • Yeah, I was only thinking about the specific case - in general, the NotImplemented-based mechanism is quite useful. – max Sep 12 '12 at 02:37