Commit 70b64fce authored by Raymond Hettinger's avatar Raymond Hettinger

Issue #1771: Remove cmp parameter from list.sort() and builtin.sorted().

parent 4f066126
......@@ -959,31 +959,20 @@ available. They are listed here in alphabetical order.
``a[start:stop:step]`` or ``a[start:stop, i]``.
.. function:: sorted(iterable[, cmp[, key[, reverse]]])
.. function:: sorted(iterable[, key[, reverse]])
Return a new sorted list from the items in *iterable*.
The optional arguments *cmp*, *key*, and *reverse* have the same meaning as
The optional arguments *key* and *reverse* have the same meaning as
those for the :meth:`list.sort` method (described in section
:ref:`typesseq-mutable`).
*cmp* specifies a custom comparison function of two arguments (iterable
elements) which should return a negative, zero or positive number depending on
whether the first argument is considered smaller than, equal to, or larger than
the second argument: ``cmp=lambda x,y: cmp(x.lower(), y.lower())``. The default
value is ``None``.
*key* specifies a function of one argument that is used to extract a comparison
key from each list element: ``key=str.lower``. The default value is ``None``.
*reverse* is a boolean value. If set to ``True``, then the list elements are
sorted as if each comparison were reversed.
In general, the *key* and *reverse* conversion processes are much faster than
specifying an equivalent *cmp* function. This is because *cmp* is called
multiple times for each list element while *key* and *reverse* touch each
element only once.
.. function:: staticmethod(function)
......
......@@ -1266,8 +1266,7 @@ Note that while lists allow their items to be of any type, bytearray object
| ``s.reverse()`` | reverses the items of *s* in | \(6) |
| | place | |
+------------------------------+--------------------------------+---------------------+
| ``s.sort([cmp[, key[, | sort the items of *s* in place | (6), (7) |
| reverse]]])`` | | |
| ``s.sort([key[, reverse]])`` | sort the items of *s* in place | (6), (7) |
+------------------------------+--------------------------------+---------------------+
.. index::
......@@ -1321,23 +1320,12 @@ Notes:
The :meth:`sort` method takes optional arguments for controlling the
comparisons.
*cmp* specifies a custom comparison function of two arguments (list items) which
should return a negative, zero or positive number depending on whether the first
argument is considered smaller than, equal to, or larger than the second
argument: ``cmp=lambda x,y: cmp(x.lower(), y.lower())``. The default value
is ``None``.
*key* specifies a function of one argument that is used to extract a comparison
key from each list element: ``key=str.lower``. The default value is ``None``.
*reverse* is a boolean value. If set to ``True``, then the list elements are
sorted as if each comparison were reversed.
In general, the *key* and *reverse* conversion processes are much faster than
specifying an equivalent *cmp* function. This is because *cmp* is called
multiple times for each list element while *key* and *reverse* touch each
element only once.
Starting with Python 2.3, the :meth:`sort` method is guaranteed to be stable. A
sort is stable if it guarantees not to change the relative order of elements
that compare equal --- this is helpful for sorting in multiple passes (for
......
......@@ -1255,8 +1255,7 @@ class CookieJar:
"""
# add cookies in order of most specific (ie. longest) path first
def decreasing_size(a, b): return cmp(len(b.path), len(a.path))
cookies.sort(decreasing_size)
cookies.sort(key=lambda a: len(a.path), reverse=True)
version_set = False
......
......@@ -238,7 +238,7 @@ class Stats:
stats_list.append((cc, nc, tt, ct) + func +
(func_std_string(func), func))
stats_list.sort(TupleComp(sort_tuple).compare)
stats_list.sort(key=CmpToKey(TupleComp(sort_tuple).compare))
self.fcn_list = fcn_list = []
for tuple in stats_list:
......@@ -470,6 +470,16 @@ class TupleComp:
return direction
return 0
def CmpToKey(mycmp):
'Convert a cmp= function into a key= function'
class K(object):
def __init__(self, obj):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) == -1
return K
#**************************************************************************
# func_name is a triple (file:string, line:int, name:string)
......
......@@ -8,6 +8,15 @@ import os
import unittest
from test import test_support, seq_tests
def CmpToKey(mycmp):
'Convert a cmp= function into a key= function'
class K(object):
def __init__(self, obj):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) == -1
return K
class CommonTest(seq_tests.CommonTest):
def test_init(self):
......@@ -430,23 +439,21 @@ class CommonTest(seq_tests.CommonTest):
def revcmp(a, b):
return cmp(b, a)
u.sort(revcmp)
u.sort(key=CmpToKey(revcmp))
self.assertEqual(u, self.type2test([2,1,0,-1,-2]))
# The following dumps core in unpatched Python 1.5:
def myComparison(x,y):
return cmp(x%3, y%7)
z = self.type2test(range(12))
z.sort(myComparison)
z.sort(key=CmpToKey(myComparison))
self.assertRaises(TypeError, z.sort, 2)
def selfmodifyingComparison(x,y):
z.append(1)
return cmp(x, y)
self.assertRaises(ValueError, z.sort, selfmodifyingComparison)
self.assertRaises(TypeError, z.sort, lambda x, y: 's')
self.assertRaises(ValueError, z.sort, key=CmpToKey(selfmodifyingComparison))
self.assertRaises(TypeError, z.sort, 42, 42, 42, 42)
......
......@@ -1764,9 +1764,6 @@ class TestSorted(unittest.TestCase):
data.reverse()
random.shuffle(copy)
self.assertEqual(data, sorted(copy, cmp=lambda x, y: (x < y) - (x > y)))
self.assertNotEqual(data, copy)
random.shuffle(copy)
self.assertEqual(data, sorted(copy, key=lambda x: -x))
self.assertNotEqual(data, copy)
random.shuffle(copy)
......
......@@ -6,6 +6,15 @@ import unittest
verbose = test_support.verbose
nerrors = 0
def CmpToKey(mycmp):
'Convert a cmp= function into a key= function'
class K(object):
def __init__(self, obj):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) == -1
return K
def check(tag, expected, raw, compare=None):
global nerrors
......@@ -14,7 +23,7 @@ def check(tag, expected, raw, compare=None):
orig = raw[:] # save input in case of error
if compare:
raw.sort(compare)
raw.sort(key=CmpToKey(compare))
else:
raw.sort()
......@@ -99,7 +108,7 @@ class TestBase(unittest.TestCase):
print(" Checking against an insane comparison function.")
print(" If the implementation isn't careful, this may segfault.")
s = x[:]
s.sort(lambda a, b: int(random.random() * 3) - 1)
s.sort(key=CmpToKey(lambda a, b: int(random.random() * 3) - 1))
check("an insane function left some permutation", x, s)
x = [Complains(i) for i in x]
......@@ -141,14 +150,6 @@ class TestBugs(unittest.TestCase):
L = [C() for i in range(50)]
self.assertRaises(ValueError, L.sort)
def test_cmpNone(self):
# Testing None as a comparison function.
L = list(range(50))
random.shuffle(L)
L.sort(None)
self.assertEqual(L, list(range(50)))
def test_undetected_mutation(self):
# Python 2.4a1 did not always detect mutation
memorywaster = []
......@@ -158,12 +159,12 @@ class TestBugs(unittest.TestCase):
L.pop()
return cmp(x, y)
L = [1,2]
self.assertRaises(ValueError, L.sort, mutating_cmp)
self.assertRaises(ValueError, L.sort, key=CmpToKey(mutating_cmp))
def mutating_cmp(x, y):
L.append(3)
del L[:]
return cmp(x, y)
self.assertRaises(ValueError, L.sort, mutating_cmp)
self.assertRaises(ValueError, L.sort, key=CmpToKey(mutating_cmp))
memorywaster = [memorywaster]
#==============================================================================
......@@ -175,11 +176,11 @@ class TestDecorateSortUndecorate(unittest.TestCase):
copy = data[:]
random.shuffle(data)
data.sort(key=str.lower)
copy.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
copy.sort(key=CmpToKey(lambda x,y: cmp(x.lower(), y.lower())))
def test_baddecorator(self):
data = 'The quick Brown fox Jumped over The lazy Dog'.split()
self.assertRaises(TypeError, data.sort, None, lambda x,y: 0)
self.assertRaises(TypeError, data.sort, key=lambda x,y: 0)
def test_stability(self):
data = [(random.randrange(100), i) for i in range(200)]
......@@ -188,25 +189,11 @@ class TestDecorateSortUndecorate(unittest.TestCase):
copy.sort() # sort using both fields
self.assertEqual(data, copy) # should get the same result
def test_cmp_and_key_combination(self):
# Verify that the wrapper has been removed
def compare(x, y):
self.assertEqual(type(x), str)
self.assertEqual(type(x), str)
return cmp(x, y)
data = 'The quick Brown fox Jumped over The lazy Dog'.split()
data.sort(cmp=compare, key=str.lower)
def test_badcmp_with_key(self):
# Verify that the wrapper has been removed
data = 'The quick Brown fox Jumped over The lazy Dog'.split()
self.assertRaises(TypeError, data.sort, "bad", str.lower)
def test_key_with_exception(self):
# Verify that the wrapper has been removed
data = list(range(-2, 2))
dup = data[:]
self.assertRaises(ZeroDivisionError, data.sort, None, lambda x: 1/x)
self.assertRaises(ZeroDivisionError, data.sort, key=lambda x: 1/x)
self.assertEqual(data, dup)
def test_key_with_mutation(self):
......@@ -254,14 +241,13 @@ class TestDecorateSortUndecorate(unittest.TestCase):
random.shuffle(data)
data.sort(reverse=True)
self.assertEqual(data, list(range(99,-1,-1)))
self.assertRaises(TypeError, data.sort, "wrong type")
def test_reverse_stability(self):
data = [(random.randrange(100), i) for i in range(200)]
copy1 = data[:]
copy2 = data[:]
data.sort(cmp=lambda x,y: cmp(x[0],y[0]), reverse=True)
copy1.sort(cmp=lambda x,y: cmp(y[0],x[0]))
data.sort(key=CmpToKey(lambda x,y: cmp(x[0],y[0])), reverse=True)
copy1.sort(key=CmpToKey(lambda x,y: cmp(y[0],x[0])))
self.assertEqual(data, copy1)
copy2.sort(key=lambda x: x[0], reverse=True)
self.assertEqual(data, copy2)
......
......@@ -14,6 +14,8 @@ Core and Builtins
- Issue #1973: bytes.fromhex('') raises SystemError
- Issue #1771: remove cmp parameter from sorted() and list.sort()
- Issue #1969: split and rsplit in bytearray are inconsistent
- map() and itertools.imap() no longer accept None for the first argument.
......
This diff is collapsed.
......@@ -1494,14 +1494,14 @@ Precision may be negative.");
static PyObject *
builtin_sorted(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *newlist, *v, *seq, *compare=NULL, *keyfunc=NULL, *newargs;
PyObject *newlist, *v, *seq, *keyfunc=NULL, *newargs;
PyObject *callable;
static char *kwlist[] = {"iterable", "cmp", "key", "reverse", 0};
static char *kwlist[] = {"iterable", "key", "reverse", 0};
int reverse;
/* args 1-4 should match listsort in Objects/listobject.c */
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOi:sorted",
kwlist, &seq, &compare, &keyfunc, &reverse))
/* args 1-3 should match listsort in Objects/listobject.c */
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oi:sorted",
kwlist, &seq, &keyfunc, &reverse))
return NULL;
newlist = PySequence_List(seq);
......@@ -1533,7 +1533,7 @@ builtin_sorted(PyObject *self, PyObject *args, PyObject *kwds)
}
PyDoc_STRVAR(sorted_doc,
"sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list");
"sorted(iterable, key=None, reverse=False) --> new sorted list");
static PyObject *
builtin_vars(PyObject *self, PyObject *args)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment