Python 3 __cmp__ semantic change?

Arnaud Delobelle arnodel at googlemail.com
Fri Nov 21 12:26:21 EST 2008


Hyuga <hyugaricdeau at gmail.com> writes:

> On Nov 21, 4:09 am, Duncan Booth <duncan.bo... at invalid.invalid> wrote:
>> Johannes Bauer <dfnsonfsdu... at gmx.de> wrote:
>> > Seems it was removed on purpose - I'm sure there was a good reason for
>> > that, but may I ask why? Instead of the sleek __cmp__ function I had
>> > earlier, I now have code like:
>>
>> > def __lt__(self, other):
>> >      return self.__cmp__(other) < 0
>>
>> > def __le__(self, other):
>> >      return self.__cmp__(other) < 0
>>
>> I hope you actually have <= here.
>>
>>
>>
>> > def __gt__(self, other):
>> >      return self.__cmp__(other) > 0
>>
>> > def __ge__(self, other):
>> >      return self.__cmp__(other) >= 0
>>
>> > Does anyone know the reason why __cmp__ was discarded?
>>
>> I think it was because __cmp__ was the backward compatible fallback for
>> the newer rich comparison methods and Python 3 cleans up a lot of stuff
>> left in just for backward compatibility. In this case it is a cleanup
>> too far as in most cases (i.e. those cases where you don't need the full
>> complexity of the rich comparisons) __cmp__ is a much simpler solution.
>>
>> Seehttp://mail.python.org/pipermail/python-dev/2003-March/034073.html
>> for Guido's original thoughts. Also, once upon a time pep-3000
>> referred to the removal of __cmp__ but I can't find it in any of the
>> current peps. See
>> http://mail.python.org/pipermail/python-checkins/2004-August/042959.html
>> and
>> http://mail.python.org/pipermail/python-checkins/2004-August/042972.html
>> where the reference to removal of __cmp__ became "Comparisons other
>> than ``==`` and ``!=`` between disparate types will raise an
>> exception unless explicitly supported by the type" and the reference
>> to Guido's email about removing __cmp__ was also removed.
>
> Guido's primary argument for removing it seems to be that the code for
> supporting both __cmp__ and the rich comparisons is "hairy" and that
> it felt really satisfying to remove.  I don't think that's a good
> enough argument.  It was hairy because there are a lot of cases to
> check, but I wouldn't say it was crufty.  It made sense, and the way
> it worked seemed logical enough.  I never ran into any problems with
> it.  And by and far the most common case is to implement some total
> ordering for a class.
>
> Now, as has been pointed out, all you really need to define total
> ordering, at least for sorting, is __eq__ and __lt__, which isn't too
> bad.  But you still lose the ability to make any other sort of
> comparison without implementing all the other comparison operators
> too.

As classes can be decorated in Python 3, you can write a decorator to
make a class totally ordered.  Here is a very simplified proof of
concept such decorator:

def totally_ordered(cls):
    if not hasattr(cls, '__gt__'):
        def gt(self, other):
            return self != other and not self < other
        cls.__gt__ = gt
    # Do the same with __le__, __ge__
    return cls


@totally_ordered
class Fraction:
    def __init__(self, num, den=1):
        assert den > 0, "denomintator must be > 0"
        self.num = num
        self.den = den
    def __eq__(self, other):
        return self.num*other.den == self.den*other.num
    def __lt__(self, other):
        return self.num*other.den < self.den*other.num

>>> q12=Fraction(1, 2)
>>> q23=Fraction(2, 3)
>>> q12 < q23
True
>>> q12 > q23
False

Granted it's not as efficient as a __cmp__ function.

-- 
Arnaud



More information about the Python-list mailing list