Why Can't I Use the Method _Cmp_ in Python 3 as for Python 2

Why can't I use the method __cmp__ in Python 3 as for Python 2?

You need to provide the rich comparison methods for ordering in Python 3, which are __lt__, __gt__, __le__, __ge__, __eq__, and __ne__. See also: PEP 207 -- Rich Comparisons.

__cmp__ is no longer used.


More specifically, __lt__ takes self and other as arguments, and needs to return whether self is less than other. For example:

class Point(object):
...
def __lt__(self, other):
return ((self.x < other.x) and (self.y < other.y))

(This isn't a sensible comparison implementation, but it's hard to tell what you were going for.)

So if you have the following situation:

p1 = Point(1, 2)
p2 = Point(3, 4)

p1 < p2

This will be equivalent to:

p1.__lt__(p2)

which would return True.

__eq__ would return True if the points are equal and False otherwise. The other methods work analogously.


If you use the functools.total_ordering decorator, you only need to implement e.g. the __lt__ and __eq__ methods:

from functools import total_ordering

@total_ordering
class Point(object):
def __lt__(self, other):
...

def __eq__(self, other):
...

How to use cmp() in Python 3?

As mentioned in the comments, cmp doesn't exist in Python 3. If you really want it, you could define it yourself:

def cmp(a, b):
return (a > b) - (a < b)

which is taken from the original What's New In Python 3.0. It's pretty rare -- though not unheard of -- that it's really needed, though, so you might want to think about whether it's actually the best way to do whatever it is you're up to.

Python cmp() function

The cmp function is only in Python 2.x. As mentioned in the official Python documentation:

The cmp() function should be treated as gone, and the __cmp__() special method is no longer supported. Use __lt__() for sorting, __eq__() with __hash__(), and other rich comparisons as needed. (If you really need the cmp() functionality, you could use the expression (a > b) - (a < b) as the equivalent for cmp(a, b).)

The cmp equivalent in Python 3.x is:

def cmp(a, b):
return (a > b) - (a < b)

Note: Your tuples (myStupidTup and mySmartTup) don't support comparison. You will get a TypeError if you run it: TypeError: '>' not supported between instances of 'str' and 'int'

Why is the cmp parameter removed from sort/sorted in Python3.0?

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.

Use Python 2 Dict Comparison in Python 3

If you want the same behavior as earlier versions of Python 2.x in both 2.7 (which uses an arbitrary sort order instead) and 3.x (which refuses to sort dicts), Ned Batchelder's answer to a question about how sorting dicts works gets you part of the way there, but not all the way.


First, it gives you an old-style cmp function, not a new-style key function. Fortunately, both 2.7 and 3.x have functools.cmp_to_key to solve that. (You could of course instead rewrite the code as a key function, but that may make it harder to see any differences between the posted code and your code…)


More importantly, it not only doesn't do the same thing in 2.7 and 3.x, it doesn't even work in 2.7 and 3.x. To understand why, look at the code:

def smallest_diff_key(A, B):
"""return the smallest key adiff in A such that A[adiff] != B[bdiff]"""
diff_keys = [k for k in A if A.get(k) != B.get(k)]
return min(diff_keys)

def dict_cmp(A, B):
if len(A) != len(B):
return cmp(len(A), len(B))
adiff = smallest_diff_key(A, B)
bdiff = smallest_diff_key(B, A)
if adiff != bdiff:
return cmp(adiff, bdiff)
return cmp(A[adiff], b[bdiff])

Notice that it's calling cmp on the mismatched values.

If the dicts can contain other dicts, that's relying on the fact that cmp(d1, d2) is going to end up calling this function… which is obviously not true in newer Python.

On top of that, in 3.x cmp doesn't even exist anymore.

Also, this relies on the fact that any value can be compared with any other value—you might get back arbitrary results, but you won't get an exception. That was true (except in a few rare cases) in 2.x, but it's not true in 3.x. That may not be a problem for you if you don't want to compare dicts with non-comparable values (e.g., if it's OK for {1: 2} < {1: 'b'} to raise an exception), but otherwise, it is.

And of course if you don't want arbitrary results for dict comparison, do you really want arbitrary results for value comparisons?

The solution to all three problems is simple: you have to replace cmp, instead of calling it. So, something like this:

def mycmp(A, B):
if isinstance(A, dict) and isinstance(B, dict):
return dict_cmp(A, B)
try:
return A < B
except TypeError:
# what goes here depends on how far you want to go for consistency

If you want the exact rules for comparison of objects of different types that 2.7 used, they're documented, so you can implement them. But if you don't need that much detail, you can write something simpler here (or maybe even just not trap the TypeError, if the exception mentioned above is acceptable).

How to use sorted() function?

As mentioned in the sorted docs, you need to pass your comparison function as a cmp argument not a key argument. So

sorted(some_list, cmp=cmp_ignore_case)

However, using cmp is much less efficient than key, and so cmp has been deprecated for some time in Python 2, and it has been removed from Python 3.

A key function only gets called once for each unique item in the list (or other iterable) that you're sorting. In contrast, a cmp function must be called every time a comparison is made between two items.

It's possible to transform a cmp function into a key function with functools.cmp_to_key, but this is only provided for convenience: it doesn't prevent the inefficiency, it only hides it. So it should only be used as a temporary fix for old code that uses cmp, or in those rare cases where it's not possible to use a key function.

For further details, please see the Sorting HOW TO.



Related Topics



Leave a reply



Submit