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