44

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?

smci
  • 32,567
  • 20
  • 113
  • 146
brain storm
  • 30,124
  • 69
  • 225
  • 393
  • 3
    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? – abarnert Nov 25 '13 at 20:26
  • (Actually, there's a third thing you may be confusing, the [`cmp`](http://docs.python.org/2/library/functions.html#cmp) function, also removed in 3.x.) – abarnert Nov 25 '13 at 20:35
  • all of kinds of cmp are removed in 3.X or it is not recommended to use..correct? – eagertoLearn Nov 25 '13 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. – abarnert Nov 25 '13 at 20:38
  • so basically forget cmp and use keys for sorting; rich comparisons for class..that is my take home message.. – eagertoLearn Nov 25 '13 at 20:39
  • 1
    @user2708477: Exactly. – abarnert Nov 25 '13 at 20:40
  • [Nice video about Python and this topic](http://youtu.be/OSGv2VnC0go?t=9m42s), I linked the start time of the part dealing with the comparison functions, but it is actually worth watching the whole video… Give it some minutes... @l4mpi: Yes, better as a comment. – koffein Nov 25 '13 at 21:27

2 Answers2

29

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.

Eevee
  • 47,412
  • 11
  • 95
  • 127
  • when you want to compare two sets like {1,2,3} vs {4,5,6}. It depends on how to define your __cmp__ correct, I can define it in a way that return first element of set 1 - first element of set 2. On the same note, how does defining rich comparison methods, solve comparing the two sets you described – brain storm Nov 25 '13 at 20:25
  • 3
    @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. – abarnert Nov 25 '13 at 20:27
  • 1
    @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}`. – abarnert Nov 25 '13 at 20:29
  • 2
    @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!) – Eevee Nov 25 '13 at 20:32
  • As a side note, when you're creating numeric types, you can use the ABCs in [`numbers`](http://docs.python.org/3/library/numbers.html) to help get the comparisons (along with everything else) right. See the [`fractions`](http://docs.python.org/3/library/fractions.html#fractions.Fraction) module, which has a link to its source code, for an example. – abarnert Nov 25 '13 at 20:32
  • Learned something in python today as I always do from SO Posts! Thank you all – eagertoLearn Nov 25 '13 at 20:34
  • One interesting thing I just learned about the C-API equivalents: Even though all code in 2.7 that needs comparisons calls the new-style functions [`PyObject_RichCompare`](http://docs.python.org/2/c-api/object.html#PyObject_RichCompare)/`PyObject_RichCompareBool`, and the old functions `PyObject_Cmp`/`PyObject_Compare` are gone in 3.x, they're still not deprecated in the 2.7 C API. – abarnert Nov 25 '13 at 20:46
  • 1
    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` – berdario Nov 29 '13 at 20:02
  • 2
    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`. – saeedgnu Feb 06 '16 at 23:27
  • 1
    `cmp` is absolutely not more flexible than `key`. there are many kinds of ordering, including some _built into the language_, that `cmp` cannot express at all. – Eevee Feb 10 '16 at 07:36
  • can I also point out that `bool(cmp(a,b))` would give the same result as `a != b` which is extremely confusing. – Tadhg McDonald-Jensen Mar 22 '16 at 17:26
  • Pointing out that `cmp` is "inflexible" because it fails to account for types that are not totally ordered is like saying subtraction is inflexible because you can't subtract lists. By definition, `cmp` is a function that only makes sense for totally ordered types. Using a key function is definitively *less* flexible: e.g. you can't reverse the ordering using just a key function (naively you'd try `new_key = lambda x: -old_key(x)` but that assumes keys are negatable). This is why every Python function that accepts `key` comes with a `reverse` argument to compensate for the inadequacy. – Rufflewind Dec 13 '16 at 19:16
  • Not satisfied. I defined my own cmp= when I knew what I was sorting. I understand the argument but I do not agree with this direction at all. Also cmp_to_key which is recommended is very much less efficient than my lambda function. – Samantha Atkins Jul 11 '18 at 00:01
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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 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 :) – mgilson Nov 25 '13 at 20:19
  • for custom classes, why is it recommended not to use `cmp`? but instead `__ge__, __lt__` etc.. – brain storm Nov 25 '13 at 20:20
  • @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__`. – abarnert Nov 25 '13 at 20:34
  • 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. – abarnert Nov 25 '13 at 20:37
  • @abarnert: ah, I meant to mention 'reverse' there. – Martijn Pieters Nov 25 '13 at 20:41
  • @functools.total_ordering looks like a class decorator than function decorator. I have not used this previously. so just confirming it – brain storm Nov 25 '13 at 20:47
  • 1
    @user1988876: `@functools.total_ordering` is indeed a class decorator. – Martijn Pieters Nov 25 '13 at 20:49