Perceived inconsistency in py3k documentation

Peter Otten __peter__ at web.de
Sun Dec 5 05:37:23 EST 2010


Greg wrote:

> This is my first post here, so if this is not the correct place to ask
> this, please direct me to the best place.

This is a good place to get general advice and to discuss potential bugs 
when you are unsure whether they actually are bugs.
If you are sure that you ran into a bug in python or want to suggest an 
improvement of the documentation where it is wrong or unclear or hard to 
understand you can report to http://bugs.python.org .
 
> In looking at the py3k documentation for comparing two classes, two
> different view points are expressed (at least it seems so to me).
> 1) At http://docs.python.org/py3k/reference/datamodel.html:
>   "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__()..."
>   -- This seems to support the view that if in our code, we would like
> to use comparison operators <, >, =, !=, etc. then we should define a
> __lt__(), __gt__(), __eq__(), __ne__(), etc. for each comparison
> operator we would like.
> 
> This appears to contrast
> 2) At http://docs.python.org/py3k/library/stdtypes.html:
>   "Instances of a class cannot be ordered with respect to other
> instances of the same class, or other types of object, unless the
> class defines enough of the methods __lt__(), __le__(), __gt__(), and
> __ge__() (in general, __lt__() and __eq__() are sufficient, if you
> want the conventional meanings of the comparison operators)."
>   -- This seems to imply that to get all of the operators, only
> __lt__() and __eq__() need to be defined (just __lt__() should suffice
> though I thought).
> 
> So, which is it supposed to be? Or am I reading the documentation
> wrong?

I agree with you that the documentation is at least unclear. The following 
experiment suggests that list.sort() works correctly if only __lt__() and 
__eq__() are implemented which in my reading is what your second quote 
intends to convey. But "enough of the methods..." is a fuzzy statement.

The other finding:
(1) a != b is emulated with not (a == b) 
(2) a > b is emulated with b < a
is not something I'd expect after reading your first quote, but technically 
(2) is covered by

"""... __lt__() and __gt__() are each other’s reflection ..."""

$ cat py3compare.py
report = True      

class A:
    def __init__(self, key, side="A"):
        self.key = key                
        self.side = side              
    def __eq__(self, other):          
        result = self.key == other.key
        if report:                    
            print(self, "__eq__", other, "-->", result)
        return result                                  
    def __lt__(self, other):
        result = self.key < other.key
        if report:
            print(self, "__lt__", other, "-->", result)
        return result
    def __str__(self):
        return "{}({})".format(self.side, self.key)
    def __repr__(self):
        return str(self.key)

a = A(1, "L")
for k in range(3):
    b = A(k, "R")
    print("{} != {}: {}\n".format(a, b, a != b))
    print("{} > {}: {}\n".format(a, b, a > b))
    print()

import random
items = []
for n in 10, 20:
    items.extend(map(A, range(n)))
random.shuffle(items)

report = False
items.sort()
print(items)

print(a <= b)
$ python3 py3compare.py
L(1) __eq__ R(0) --> False
L(1) != R(0): True

R(0) __lt__ L(1) --> True
L(1) > R(0): True


L(1) __eq__ R(1) --> True
L(1) != R(1): False

R(1) __lt__ L(1) --> False
L(1) > R(1): False


L(1) __eq__ R(2) --> False
L(1) != R(2): True

R(2) __lt__ L(1) --> False
L(1) > R(2): False


[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 11, 12, 13, 
14, 15, 16, 17, 18, 19]
Traceback (most recent call last):
  File "py3compare.py", line 39, in <module>
    print(a <= b)
TypeError: unorderable types: A() <= A()
$

Conclusion: If you can come up with a text that is both correct and clear, 
don't hesitate to tell us, here or on the bug tracker.

Peter

PS: The painless way out: always use @functools.total_ordering or the 
equivalent cookbok recipe.



More information about the Python-list mailing list