[Python-Dev] Python docs about comparisons vs. CPython reality (original) (raw)

Jan Kaliszewski zuo at kaliszewski.net
Sat Sep 6 13:34:37 CEST 2014


Hello,

Are they bugs in the Python docs or just some CPython implementation details that are purposely not documented? (but then, again, some of the docs seem to be at least not precise...):

In https://docs.python.org/3.4/reference/datamodel.html#object._eq_ there is the statement:

There are no implied relationships among the comparison operators. The truth of x==y does not imply that x!=y is false. Accordingly, when defining eq(), one should also define ne() so that the operators will behave as expected.

On the other hand, in https://docs.python.org/3.4/library/stdtypes.html#comparisons we read:

(in general, lt() and eq() are sufficient, if you want the conventional meanings of the comparison operators)

And, when I try the eq() stuff in CPython it seems that, indeed, the language provides a proper ne() implementation for me automatically (without need to implement ne() explicitly by myself):

Python 3.4.0 (default, Mar 20 2014, 01:28:00) 
[...]
>>> class A:
...     def __eq__(self, other):
...         if hasattr(self, 'x') and hasattr(other, 'x'):
...             return self.x == other.x
...         return NotImplemented
... 
>>> A() == A()
False
>>> A() != A()
True
>>> a = A()
>>> a.x = 1
>>> a1 = A()
>>> a1.x = 1
>>> a2 = A()
>>> a2.x = 2
>>> a == a1
True
>>> a != a1
False
>>> a1 == a1
True
>>> a1 != a1
False
>>> a1 == a2
False
>>> a1 != a2
True

Is it a language guarantee (then, I believe, it should be documented) or just an implementation accident? (then, I believe, it still could be documented as a CPython implementation detail). See also the Python equivalent of the SimpleNamespace class (without ne() implemented explicitly): https://docs.python.org/3/library/types.html#types.SimpleNamespace

On the other hand, the "lt() and eq() are sufficient" statement seems not to be true:

>>> a < a1
False
>>> a <= a1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: A() <= A()
>>> a > a1
False
>>> a >= a1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: A() >= A()
>>> a1 < a2
True
>>> a1 <= a2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: A() <= A()
>>> a1 > a2
False
>>> a1 >= a2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: A() >= A()

On yet another hand, adding le() to that class seems to be perfectly sufficient (without adding gt() and ge()):

>>> def le(self, other):
...     if hasattr(self, 'x') and hasattr(other, 'x'):
...         return self.x <= other.x
...     return NotImplemented
... 
>>> A.__le__ = le
>>> a < a1
False
>>> a <= a1
True
>>> a > a1
False
>>> a >= a1
True
>>> a1 < a2
True
>>> a1 <= a2
True
>>> a1 > a2
False
>>> a1 >= a2
False

What of all this stuff is a language guarantee and what is just an implementation accident?

Shouldn't it be documented more accurately?

Cheers. *j



More information about the Python-Dev mailing list