[Python-Dev] Python docs about comparisons vs. CPython reality

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