python / cpython

The Python programming language
https://www.python.org/
Other
60.06k stars 29.09k forks source link

Reference 3.3.1 Basic Customization #118457

Closed sdachen closed 2 weeks ago

sdachen commented 2 weeks ago

Original paragraph from this page:

By default, object implements eq() by using is, returning NotImplemented in the case of a false comparison: True if x is y else NotImplemented.

--

If I understand correctly, this means if no __eq__() is implemented for a custom class, it'll defaultobject.__eq__() to the implementation True if x is y else NotImplemented.

However, upon testing, I found the behavior to be different.

class A:
  def __init__(self, x):
    self.x = x

class B:
  pass

def documentation_eq(x, y):
  return True if x is y else NotImplemented

if __name__ == "__main__":
  a11 = A(1)
  a12 = A(1)
  a2 = A(2)
  b = B()
  print('a11 == a11 evaluates to {}'.format(a11 == a11))
  print('a11 == a12 evaluates to {}'.format(a11 == a12))
  print('a11 == a2 evaluates to {}'.format(a11 == a2))
  print('a11 == b evaluates to {}'.format(a11 == b))
  print('documentation_eq(a11, a11) evaluates to {}'.format(documentation_eq(a11, a11)))
  print('documentation_eq(a11, a12) evaluates to {}'.format(documentation_eq(a11, a12)))
  print('documentation_eq(a11, a2) evaluates to {}'.format(documentation_eq(a11, a2)))
  print('documentation_eq(a11, b) evaluates to {}'.format(documentation_eq(a11, b)))

The program yields

a11 == a11 evaluates to True
a11 == a12 evaluates to False
a11 == a2 evaluates to False
a11 == b evaluates to False
documentation_eq(a11, a11) evaluates to True
documentation_eq(a11, a12) evaluates to NotImplemented
documentation_eq(a11, a2) evaluates to NotImplemented
documentation_eq(a11, b) evaluates to NotImplemented

Showing an inconsistency between the code from documentation and the actual implementation in Python 3.12.3 (main, Apr 9 2024, 08:09:14) [Clang 15.0.0 (clang-1500.3.9.4)].

sdachen commented 2 weeks ago

Upon further testing, I found

>>> A.__eq__(a11, a12)
NotImplemented
>>> A.__eq__(a11, a11)
True
>>> a11.__eq__(a12)
NotImplemented
>>> a11.__eq__(a11)
True

Looks like it's not really a bug in the documentation, but why would a11 == a12 not call a11.__eq__(a12) here?

sdachen commented 2 weeks ago

Reopening for another paragraph that mentions x==y calls x.__eq__(y).

These are the so-called “rich comparison” methods. The correspondence between operator symbols and method names is as follows: x<y calls x.__lt__(y), x<=y calls x.__le__(y), x==y calls x.__eq__(y), x!=y calls x.__ne__(y), x>y calls x.__gt__(y), and x>=y calls x.__ge__(y).

sdachen commented 2 weeks ago

I think maybe it's because the last paragraph of the fall back behavior:

When no appropriate method returns any value other than NotImplemented, the == and != operators will fall back to is and is not, respectively.