排序穩定性和排序複雜度
排序保證是 穩定 的。 這意味着當多個記錄具有相同的鍵值時,將保留其原始順序。
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
>>> sorted(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]
注意 blue 的兩個記錄如何保留它們的原始順序,以便 ('blue', 1)
保證在 ('blue', 2)
之前。
這個美妙的屬性允許你在一系列排序步驟中構建複雜的排序。例如,要按 grade 降序然後 age 升序對學生數據進行排序,請先 age 排序,然後再使用 grade 排序:
>>> s = sorted(student_objects, key=attrgetter('age')) # sort on secondary key
>>> sorted(s, key=attrgetter('grade'), reverse=True) # now sort on primary key, descending
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
這可以被抽象爲一個包裝函數,該函數能接受一個列表以及字段和順序的元組,以對它們進行多重排序。
>>> def multisort(xs, specs):
... for key, reverse in reversed(specs):
... xs.sort(key=attrgetter(key), reverse=reverse)
... return xs
>>> multisort(list(student_objects), (('grade', True), ('age', False)))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
Python 中使用的 Timsort 算法可以有效地進行多種排序,因爲它可以利用數據集中已存在的任何排序。
使用 cmp 參數的舊方法
本 HOWTO 中給出的許多結構都假定爲 Python 2.4 或更高版本。在此之前,沒有內置 sorted()
, list.sort()
也沒有關鍵字參數。相反,所有 Py2.x 版本都支持 cmp 參數來處理用戶指定的比較函數。
在 Py3.0 中, cmp 參數被完全刪除(作爲簡化和統一語言努力的一部分,消除了豐富的比較與 __cmp__()
魔術方法之間的衝突)。
在 Py2.x 中, sort 允許一個可選函數,可以調用它來進行比較。該函數應該採用兩個參數進行比較,然後返回負值爲小於,如果它們相等則返回零,或者返回大於大於的正值。例如,我們可以這樣做:
>>> def numeric_compare(x, y):
... return x - y
>>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
[1, 2, 3, 4, 5]
或者你可反轉比較的順序:
>>> def reverse_numeric(x, y):
... return y - x
>>> sorted([5, 2, 4, 1, 3], cmp=reverse_numeric)
[5, 4, 3, 2, 1]
將代碼從 Python 2.x 移植到 3.x 時,如果用戶提供比較功能並且需要將其轉換爲鍵函數,則會出現這種情況。 以下包裝器使這很容易:
def cmp_to_key(mycmp):
'Convert a cmp= function into a key= function'
class K:
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]
在 Python 3.2 中, functools.cmp_to_key()
函數被添加到標準庫中的 functools
模塊中。