pythonpython-3.xpython-2.xpartial-ordering

Why can't I use the method __cmp__ in Python 3 as for Python 2?


The following piece of code

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def dispc(self):
        return ('(' + str(self.x) + ',' + str(self.y) + ')')

    def __cmp__(self, other):
        return ((self.x > other.x) and (self.y > other.y))

works fine in Python 2, but in Python 3 I get an error:

>>> p=point(2,3)
>>> q=point(3,4)
>>> p>q
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: point() > point()

It only works for == and !=.


Solution

  • You need to provide the rich comparison methods for ordering in Python 3, which are __lt__, __gt__, __le__, __ge__, __eq__, and __ne__. See also: PEP 207 -- Rich Comparisons.

    __cmp__ is no longer used.


    More specifically, __lt__ takes self and other as arguments, and needs to return whether self is less than other. For example:

    class Point(object):
        ...
        def __lt__(self, other):
            return ((self.x < other.x) and (self.y < other.y))
    

    (This isn't a sensible comparison implementation, but it's hard to tell what you were going for.)

    So if you have the following situation:

    p1 = Point(1, 2)
    p2 = Point(3, 4)
    
    p1 < p2
    

    This will be equivalent to:

    p1.__lt__(p2)
    

    which would return True.

    __eq__ would return True if the points are equal and False otherwise. The other methods work analogously.


    If you use the functools.total_ordering decorator, you only need to implement e.g. the __lt__ and __eq__ methods:

    from functools import total_ordering
    
    @total_ordering
    class Point(object):
        def __lt__(self, other):
            ...
    
        def __eq__(self, other):
            ...