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 thecmp()
functionality, you could use the expression(a > b) - (a < b)
as the equivalent forcmp(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
Get Timezone from City in Python/Django
Operationalerror: Database Is Locked
How to Apply a Function on Every Row on a Dataframe
How to Find First Non-Zero Value in Every Column of a Numpy Array
Python Datetime Formatting Without Zero-Padding
Python 2 CSV Writer Produces Wrong Line Terminator on Windows
Removing Elements from a List Containing Specific Characters
Python Argparse - Add Argument to Multiple Subparsers
Modular Multiplicative Inverse Function in Python
How to Access a Dictionary Key Value Present Inside a List
Pygame How to Let Balls Collide
Pandas Select Rows and Columns Based on Boolean Condition
Memory-Efficient Built-In SQLalchemy Iterator/Generator
Can Anyone Explain Python's Relative Imports