python 3's adoption

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Thu Jan 28 21:21:28 EST 2010


On Thu, 28 Jan 2010 17:38:23 -0800, mdj wrote:

> On Jan 29, 9:47 am, Paul Boddie <p... at boddie.org.uk> wrote:
>> On 27 Jan, 13:26, Xah Lee <xah... at gmail.com> wrote:
>>
>>
>>
>> > So, for practical reasons, i think a “key” parameter is fine. But
>> > chopping off “cmp” is damaging. When your data structure is complex,
>> > its order is not embedded in some “key”. Taking out “cmp” makes it
>> > impossible to sort your data structure.
>>
>> What would annoy me if I used Python 3.x would be the apparent lack of
>> the __cmp__ method for conveniently defining comparisons between
>> instances of my own classes. Having to define all the rich comparison
>> methods frequently isn't even as much fun as it sounds.
> 
> OT, but you can always define the other operators in terms of a cmp and
> mix it in, restoring the original behaviour. Unfortunately it won't
> restore the original performance until someone comes to their senses and
> restores __cmp__

"Comes to their senses"?

There's nothing you can do with __cmp__ that you can't do better with 
rich comparisons, and plenty that rich comparisons can do that __cmp__ is 
utterly incapable of dealing with. __cmp__ is crippled since it can only 
be used for defining classes where the operators < etc return flags. It 
can't be used if you want to implement some other behaviour for the 
operators. E.g. here's a silly example:

>>> class Silly(object):
...     def __init__(self):
...             self.link = None
...     def __gt__(self, other):
...             self.link = other
...
>>> x = Silly()
>>> x > Silly()
>>> x.link
<__main__.X object at 0xb7cda74c>


More importantly, __cmp__ is only suitable for classes that implement 
total ordering. If you have a data type that does not have total 
ordering, for example sets, you can't implement it using __cmp__.

E.g.:

>>> s = set([1, 2, 3, 4])
>>> t = set([3, 4, 5, 6])
>>> s < t
False
>>> s > t
False
>>> s == t
False
>>> cmp(s, t)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot compare sets using cmp()


Sets have partial ordering, and __cmp__ is simply not up to the job of 
dealing with it.

Having two mechanisms for implementing comparisons is unnecessary. It 
adds complications to the language that we are better off without. The 
only advantage of the obsolete __cmp__ is that lazy programmers only need 
to write one method instead of six. This is an advantage, I accept that 
(hey, I'm a lazy programmer too, that's why I use Python!) but it's not a 
big advantage. If you really care about it you can create a mixin class, 
a decorator, or a metaclass to simplify creation of the methods. For 
example, a quick and dirty decorator:


>>> def make_comparisons(cls):
...     cls.__gt__ = lambda self, other: self.__cmp__(other) == 1
...     cls.__ge__ = lambda self, other: self.__cmp__(other) >= 0
...     cls.__eq__ = lambda self, other: self.__cmp__(other) == 0
...     cls.__ne__ = lambda self, other: self.__cmp__(other) != 0
...     cls.__le__ = lambda self, other: self.__cmp__(other) <= 0
...     cls.__lt__ = lambda self, other: self.__cmp__(other) == -1
...     return cls
...
>>> @make_comparisons
... class BiggerThanEverything(object):
...     def __cmp__(self, other):
...             return 1
...
>>> x = BiggerThanEverything()
>>> x > 1000
True
>>> x < 0
False


-- 
Steven



More information about the Python-list mailing list