sort、sorted、key
sorted
之前我們使用排序時是呼叫 list 內建的 sort function:
a = [3, 1, 2]
a.sort()
print(a)
但這樣 a 會被修改,如果我們希望保持 a 不變的話,要先對 a 做備份
a = [3, 1, 2]
b = a[:] # [:] 代表取整個 a 的 slice
b.sort() #
print(b)
這個時候我們可以使用 sorted(可迭代物件) 去處理
In [1]: a = [3, 1, 2]
In [2]: b = sorted(a)
In [3]: b
Out[3]: [1, 2, 3]
In [4]: a
Out[4]: [3, 1, 2]
sorted 也可以使用在 tuple 上
In [5]: a = (2, 1, 3)
In [6]: b = sorted(a)
In [7]: b
Out[7]: [1, 2, 3]
key 和 lambda
現在我們有一個 list of tuple,裡面每一項是 (姓名, 成績)。
In [8]: score = [('rilak', 59), ('melody', 100), ('hortune', 87)]
In [9]: score[0]
Out[9]: ('rilak', 59)
我們希望可以按照成績排序,但 tuple/list 的預設是按照「第一項」來排
In [11]: sorted(score)
Out[11]: [('hortune', 87), ('melody', 100), ('rilak', 59)]
In [12]: 'hortune' < 'melody' < 'rilak'
Out[12]: True
直接呼叫 sorted 去排序的結果會按照姓名來排,因為字母順序 'h' < 'm' < 'r' ,所以 horturn 在最前面,而 rilak 在最後面。
key function!
為了不直接比較元素的大小,我們要寫一個 key function 指定說:對於每一個在 list 裡面的元素,都要先經過這個 key function 再比大小。
也就是說當 list 排好後的元素是 x, y, z,我希望他符合 f(x) < f(y) < f(z),而不是 x < y < z。
以下我定義一個 function f(x),他會讀進一個 tuple x,然後回傳 x 的第二項。這時 sorted 就會按照第二項來做排序。
In [13]: def f(x):
...: return x[1]
...:
In [14]: sorted(score, key=f)
Out[14]: [('rilak', 59), ('hortune', 87), ('melody', 100)]
更多範例
假設 score 變數存放的是三次考試的成績,我們希望按照成績的總和排序。
In [15]: score = [(10, 20, 30), (0, 60, 80), (100, 100, 100)]
In [16]: def f(x):
...: return sum(x)
...:
In [17]: sorted(score)
Out[17]: [(0, 60, 80), (10, 20, 30), (100, 100, 100)]
按照姓名長度排序
In [19]: def f(x):
...: return len(x[0])
...:
In [23]: sorted(score, key=f)
Out[23]: [('rilak', 59), ('melody', 100), ('hortune', 87)]
lambda
如果你覺得「這個 function 這麼廢了吧,還要特別定義出來?」,那你可以使用 lambda 函式:
lambda x:x[1]
跟
def f(x):
return x[1]
是等價的!只是 lambda 函式沒有名字。我們直接把 lambda 當作 key function 丟進去就行了。
In [24]: sorted(score, key=lambda x:x[1])
Out[24]: [('rilak', 59), ('hortune', 87), ('melody', 100)]
reverse
sort 內的參數有兩個:key 跟 reverse。透過在參數內將 reverse 設定成 True,排序的結果就會顛倒過來:
In [25]: sorted(score, key=lambda x:x[1], reverse=True)
Out[25]: [('melody', 100), ('hortune', 87), ('rilak', 59)]