46

from python wiki: In Py3.0, the cmp parameter was removed entirely (as part of a larger effort to simplify and unify the language, eliminating the conflict between rich comparisons and the __cmp__ methods).

I do not understand the reasoning why cmp is removed in py3.0

consider this example:

>>> def numeric_compare(x, y):
        return x - y
>>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
[1, 2, 3, 4, 5]

and now consider this version (recommended and compatible with 3.0):

def cmp_to_key(mycmp):
    'Convert a cmp= function into a key= function'
    class K(object):
        def __init__(self, obj, *args):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        def __ne__(self, other):
            return mycmp(self.obj, other.obj) != 0
    return K

>>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric))
[5, 4, 3, 2, 1]

The latter is very verbose and the same purpose is achieved in the former with just one line. On another note, I am writing my custom class for which I want to write the __cmp__ method. from my little reading across web, it is recommended to write __lt__,__gt__,__eq__,__le__,__ge__,__ne__ and not __cmp__ Again, why this recommendation? can I not just define __cmp__ making life simpler?

7
  • 4
    You're asking about two different things, the __cmp__ method to make classes comparable, and the cmp keyword argument to sorting functions to customize the sorting. Of course they're not totally unrelated, but they're not the same thing by any means. When you write a cmp function that compares your objects, it doesn't care whether it's using __cmp__ or __lt__ to do so; when you write a key function that creates key values for your objects, it doesn't care whether it's using __cmp__ or __lt__ (or neither) to do so. So, which of the two questions are you asking? Commented Nov 25, 2013 at 20:26
  • (Actually, there's a third thing you may be confusing, the cmp function, also removed in 3.x.) Commented Nov 25, 2013 at 20:35
  • all of kinds of cmp are removed in 3.X or it is not recommended to use..correct? Commented Nov 25, 2013 at 20:37
  • @user2708477: Right, the __cmp__ special method is never called, there is no cmp parameter to any of the sorting-related functions, and there is no builtin cmp function. Commented Nov 25, 2013 at 20:38
  • 1
    @user2708477: Exactly. Commented Nov 25, 2013 at 20:40

2 Answers 2

30

For two objects a and b, __cmp__ requires that one of a < b, a == b, and a > b is true. But that might not be the case: consider sets, where it's very common that none of those are true, e.g. {1, 2, 3} vs {4, 5, 6}.

So __lt__ and friends were introduced. But that left Python with two separate ordering mechanisms, which is kind of ridiculous, so the less flexible one was removed in Python 3.

You don't actually have to implement all six comparison methods. You can use the @total_ordering decorator and only implement __lt__ and __eq__.

edit: Also note that, in the case of sorting, key functions can be more efficient than cmp: in the example you gave, Python may have to call your Python comparison function O(n²) times. But a key function only needs to be called O(n) times, and if the return value is then a builtin type (as it very often is), the O(n²) pairwise comparisons go through C.

Sign up to request clarification or add additional context in comments.

13 Comments

@user1988876: Rich comparison methods solve the problem because __lt__ and __gt__ can both return False (and __le__ and __ge__ and __eq__ also return False, of course, so only __ne__ returns True). That directly represents the fact that the first set is neither less than, greater than, or equal to the second.
@user1988876: Also, this answer assumes that you know what comparing sets does in Python, but I suspect you don't actually know that. a < b for sets means that a is a proper subset of b. Which makes it obvious why you should get the results you do for {1, 2, 3} < {4, 5, 6}.
@user1988876 and this is the problem with __cmp__: you have to choose one of the three possible answers, and your first instinct is to make up something that lets you do that, but none of them are correct for those two sets. (you can't compare sets by their first elements because they're unordered!)
Something that I just realized (and imho can be a better example than comparison between disjoint sets): cmp(float('nan'), float('nan')) yields -1 even if obviously float('nan') < float('nan') is False
We should accept that cmp function is more flexible that key function, even though we can convert our cmp to key in 99% of times easily, in some cases we can never do this for sure (not in a clean way), specially when compatibility with C code is what matters. while C is still using strcmp, Python drops all types of cmp.
|
16

cmp was removed because the key attribute to .sort() and sorted() is superior in most cases. It was a hold-over from C more than anything, and was confusing to boot. Having to implement a separate __cmp__ method next to the rich comparison operators (__lt__, __gt__, etc.) was befuddling and unhelpful.

You can always use functools.cmp_to_key() to adapt an existing cmp function.

Your specific example could have been implemented without a key function, of course, as integers are already orderable; just add reverse=True.

For custom classes, use the @functools.total_ordering decorator to expand a __eq__ and one comparison operator method (e.g. __lt__, or __gt__, etc.) into a full ordering implementation.

7 Comments

And, in the same vein, functools.total_ordering could be helpful for the second bit -- Although I always thought it should live in a classtools module instead :)
for custom classes, why is it recommended not to use cmp? but instead __ge__, __lt__ etc..
@user1988876: Besides the answer I gave to that same question on the comments on the other answer, there's the fact that __cmp__ is deprecated, and will never be called if a superclass or subclass defines any of the four rich comparisons (which can lead to some fun bugs), and of course doesn't work at all in 3.x. Also, you're still confusing cmp with __cmp__.
This can't be implemented without a key, because he's comparing in reverse. (In this case, the key could just be neg, or lambda x: -x.) Well, it can be implemented without a key if you use the reverse keyword instead, but you need one of the two.
@user1988876: @functools.total_ordering is indeed a class decorator.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.