Kaydet (Commit) 70b64fce authored tarafından Raymond Hettinger's avatar Raymond Hettinger

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

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