Python 初學第十講 — 排序
依照預設或自訂的規則,實現 Python 中的資料排序
有時候我們需要對我們所擁有的資料進行排序,以便後續的查找以及使用。
和其他語言可能需要自己寫一個排序演算法不同,Python 提供了幾個便捷的函式供大家使用。在 Python 初學第六講 — 串列的更多操作當中,曾經簡單介紹過 lst.sort()
以及 sorted()
的使用。
為了避免初學者產生混淆,本篇將會以 sorted()
為主。
函式 sorted
的使用
讓我們從 sorted()
的用法開始吧!官方文件上面的使用方法介紹如下:
sorted(串列名稱, reverse=False, key=rule_fn)
sorted()
有三個參數,第一個參數是要排序的串列名稱,要注意的地方是,電腦無法判斷整數與字串之間的優先順序,因此,我們要用來排序的串列內容必須都是相同型態的元素。
第二個參數則是是否要反轉 reverse
,預設是 False。簡單來說,假如今天我們要排序的是一個整數串列, reverse=False
的意思就是由小到大排,反之,當 reverse=True
時則代表這個串列要由大到小排。
而最後一個參數則是 key
,就是指我們所要用來排序的規則。當串列是一個數字串列時(意即元素型態皆為 int
或是 float
這類的數值型態),預設的規則就是依照數值大小進行排序;而當串列的元素都是字串 str
時,預設的規則是字母順序,也就是英文字典的排序規則。
reverse
以及 key
都不是必要的參數,再不設定的情況底下都會有內建的預設值,而是當有需要使用到時再設定即可。
除此之外需要注意的事情是, sorted()
這個函式會回傳一個經過排序以後的 list
,並不會改變那個 list
本身的值。因此,如果需要使用排序過後的串列,需要另外使用一個變數來接著這個回傳的有序串列。
串列、字典的排序
接下來來看幾個範例,以便更加了解 sorted()
這個 function 的使用方式。
一、整數串列排列
舉例來說,假如我們今天有一個 list 叫做 mylist
:
mylist = [11, 6, 3, 9, 2, 1]
我們直接來對 mylist
進行排序,並且排序以後印出,用以確認排序後的串列內容,程式碼如下:
mylist = sorted(mylist)
print(mylist)
而執行結果如下:
由此可見,在經過 sorted()
以後,我們得到了一個數字順序由小到大的新的串列。那麼,如果我們這時將 reverse
設為 True
呢?
mylist = sorted(mylist, reverse=True)
print(mylist)
執行結果如下:
從執行結果可以看出,當我們將 reverse
設為 True
,排序規則就會反過來,變成由大到小進行排序。
二、字串串列排序
再來看看字串排序的例子吧!假如我們今天有一個 list
叫做 movieList
:
movieList = ["The Shape of Water", "Moonlight", "Superman", "Birdman", "Argo", "Spotlight"]
如果我們這時對其進行排序,則會得到如下的執行結果:
從 A
開頭的 Argo
開始,依照字典順序 (A to Z) 依序往下排列,當第一個字母相同時,則會繼續往下比較,直到分出先後為止。
而在 reverse=True
的情況底下,我們會得到完全相反的串列:
三、字典排序
除了串列以外, sorted()
也可以應用在字典的排序上面。字典裡的元素本身是無序的,只使用 key 值來作為 index 以便大家取用,但是有時在輸出或是運算時,我們會希望依照某個特定的順序來取用字典當中的內容。
舉例來說,有一個字典 dic
,內容如下:
dic = {"2017": "The Shape of Water", "2016": "Moonlight", "2014": "Birdman", "2015": "Spotlight"}
當我們直接 sorted(dic)
,會發生什麼事情呢?
結果會印出 [2014, 2015, 2016, 2017]
這樣的串列。由此可見,當我們直接對 dic
這個字典進行 sorted()
,我們所排序到的,只有這個字典所有的 key
值,而不是將 key
以及 value
一同下去排序,並且回傳。
那麼,如果我們想要一次將依照 key
排序好的 key
以及 value
一同印出的話,可能就需要以 for
搭配 sorted()
去取值並且輸出了。
for year in sorted(dic):
print(year + ":" dic[year])
得到輸出結果如下:
當然,還有其他對於字典可以指定排序內容的方法。
在 Python 初學第九講—字典 當中,曾經介紹過存取一個字典時,可以利用以下三個方法來取得不同的東西:
- 存取字典當中的所有
key
值,會得到一個裝著所有key
值的list
:
dict_name.keys()
2. 存取字典當中的所有 value
值,會得到一個裝著所有 value
的 list
:
dict_name.values()
3. 存取字典當中的所有 key-value
元素,會得到一個裝著所有 key-value
元素的 list
,內容物皆為形式 (key, value)
的 tuple
:
dict_name.items()
如果我們分別取得上面三種不同的內容,再進行排序的話,將會得到如下的結果:
很顯然地,當我們將 keys
以及 values
丟進 sorted()
時,就像是把字典當中的 key
或是 value
通通拿出來,丟進一個串列以後進行排序。
而當我們使用 items()
並對其進行排序時,等同於將字典當中的每個元素都拿出來,每一個 key
以及自己所對上的 value
會一起被放進一個 tuple
以後,再用這個裝滿 tuple
的 list
進行排序。當我們需要進行 tuple
的排序, sorted
會從每個 tuple
的第一個元素開始判斷,若相同才會繼續進行第二個元素的比較,因此我們所得到的結果就像是將這個字典當中的內容以 key
值進行排序。
那麼,如果我們想要得到和方才相似,但是是以 value
為依據進行排序的結果呢?我們留待下一節,自訂排序規則之後再來討論。
自訂排序規則串列
接下來我們要介紹的是,自訂排序規則。當我們今天想要對一個串列 / 字典進行排序,卻不想要使用 sorted()
預設的數值順序、字典順序來當成排序的方法而是自己想的規則時,我們就需要利用以下的方法。
一、串列自訂排序規則
如上所述, sorted()
的參數有 list name
、 reverse
、 key
三個不同的參數,前兩個已經簡單介紹過了,而最後的重頭戲就是 key
。
先舉個簡單的例子吧!假如今天我們想要排序一個字串串列,內容如下:
movieList = ["The Shape of Water", "Moonlight", "Superman", "Birdman", "Argo", "Spotlight"]
這一次我們不想要依照名字字母順序排列,而是希望能得到一個依照字串長度排序的串列。這時候我們可能就會寫下如下的程式碼:
movieList = sorted(movieList, key=len)
利用 Python 內建的 len
來計算並回傳字串長度以後, sorted()
會接住每個回傳的長度整數,並且據此來對這個串列進行排序,因此執行結果如下:
我們得到了一個由小到大,依照字串長度進行排序的串列。在 sorted()
函數當中,我們會稱呼 key
後面的函數為 key function
,也就是排序依據的函數。當 sorted()
與 key function
搭配使用, sorted()
將會把所要排序的標的串列當中的元素一個一個丟進 key function
當中,然後得到和串列元素一樣多的回傳值,再根據這些回傳值,對原串列進行排序。
就像上面範例的 len
一樣:在 sorted()
當中,作為 len
參數的,是串列當中的元素,也就是字串,而不是整個串列;回傳的也都是單個元素的長度,而不是串列中所有元素的長度。
或許我們再舉個其他例子會比較容易理解:假如今天我們一樣想針對一個字串串列進行排序,但是排序的依據是不分大小寫,將字母 A 到 Z 轉換為 1 到 26 的數字以後,將整個字串的這些數字加總,由小到大進行排序。
那麼我們要做的,可能是先寫下如下的函式:
def word2num(s):
total = 0
for character in s.lower():
total += ord(character) - 96
return(total)
Note:”Cat” 丟進函式後回傳值為 3+1+20=24。
因為不分大小寫,所以我們將字串轉成全小寫字母,然後再結合 ASCII
的編碼系統,進行字母轉數字的轉換,並且加總。最後將這個字串所代表的數字回傳。
然後使用 sorted()
來對字串串列進行排序,並且將 key
設為word2num
,這時我們的程式碼可能如下:
mylist = ["pen", "pineapplepen", "pineapple", "applepen", "apple"]print(sorted(mylist, key=word2num))
而執行結果如下:
再次提醒, sorted()
自行設定 key function
以後,會根據此 function
的回傳值進行排序,而且此 function
的參數也只是串列當中的元素,而不是整個串列。在這個範例當中, function
的參數是一個字串,而不是整個字串串列。
二、字典自訂排序
上一節討論到字典的排序時,曾經提及如果直接將字典拿去排序、或是利用 items()
當作 sorted()
的目標串列時,將會以 key
的值進行排序。那麼,當我們想要依照 value
而非 key
值進行排列時,該怎麼做呢?
讓我們直接來看做法,再進行講解吧!
sorted(dic_name.items(), key = lambda d: d[1])
我們要排序的目標串列一樣是 items
,因為可以取得所有 key-value
的 tuple
。和方才不同的地方是是 key
後面的 key function
:這裡我們使用了一種十分特別的 function ,叫做 lambda function
。
lambda function
是一種十分簡化的 function 定義方法,寫在 lambda
後面的 d
是這個 function 的參數,而在冒號 :
後方的則是這個 function 的回傳值,在這裡是 d[1]
,意思即是當這個 key function
每次拿到一個 tuple 作為參數,就取出 index 為 1 的值回傳。
我們當然也可以寫成我們所熟悉的 def
宣告函式的方法,程式碼可能如下:
def dic_value(d):
return d[1]sorted(dic.items(), key = dic_value)
執行結果完全相同,如下圖:
至此,我們已經完成了基本的排序操作。
複數條件的排序
Python 內建的 sorted()
函式在遇到條件相同的兩筆資料時,會保留原先輸入的順序而不會進行更動。舉例來說,當我們今天想要排序的是一組字串串列,而且我們想要依照字串的長度進行排序。
那麼,如同前面相同的範例,我們可能會寫出如下的程式碼:
mylist = ["pen", "pineapple", "pineapple", "applepens", "apple"]
print(sorted(mylist, key = len))
而我們所得到的執行結果如下:
可以看得出來,由於我們是依照字串長度進行排序,所以得到了一個字串由短到長排序的串列。但是, pineapple
和 applepens
兩個長度相同的字串卻是依照著輸入時的順序排列的,如果我們希望長度相同的字串應該要依照字母的順序進行排列,我們該怎麼做呢?
此時,我們最好的方法就是利用 sorted()
排序 tuple
時的規則,當函數 sorted()
要排序一個 tuple
串列時,會先判斷 tuple
當中第一個元素的優先順序,如果相同,會接著判斷下一個元素的優先順序,所以可能會寫出一個 key function
如下:
def multi_sort(s):
return (len(s), s)
這個函式會回傳字串的長度以及字串本身,因此 sorted()
得到這個函式的回傳值以後,他會先比較字串的長度,若長度相同,會接著依照字母順序比較兩個字串。而執行結果如下:
和方才不同,即使 pineapple
和 applepens
兩個字串長度相同,他們仍然會依照字母順序進行排序。
如上所述,當我們今天要進行複數條件的排序,最好的方法就是運用 tuple
的性質,如此自然可以運用任意數量的條件來進行排序。
sorted() v.s. lst.sort()
最後,讓我們來簡單討論一下關於 sorted()
以及 lst.sort()
的不同吧!
就像曾經在第六講當中提及的, sorted()
函式是將目標串列當作參數,進行排序以後,回傳一個經過排序的串列;相反的, lst.sort()
則是必須透過目標串列本身進行呼叫,因此會直接對該串列排序,沒有回傳值,而是改變該串列本身。
或許透過例子會比較清楚地表達兩者的差異:首先,我們有一個字串串列叫做 mylist
,然後我們要對其進行排序。
當我們使用 sorted()
進行排序時,得到的執行結果如下:
由此可見, sorted()
是有回傳值的,而且是將排序好的串列回傳,而且並不會改變原本串列的長相。
而當我們使用 lst.sort()
呢?執行結果如下圖:
當我們想要將 lst.sort()
的回傳值印出時,得到 None
,意即沒有回傳值。而當我們印出此時的 mylist
時,得到了一個已經經過排序的串列。
至此,Python 排序的介紹已經告一段落,善用這些排序資料的方法,將會對於資料的處理更有想法,而且更加方便。
我們是 ccClub 團隊,致力於讓 Python 成為大家的第二外語,希望能用淺顯易懂、循序漸進的方式,帶領新手一步步跨入程式設計的世界。如果你喜歡這篇文章,請給我們 1~10 個掌聲。
如果你喜歡「Python初學」的教學系列文,請給我們 11 個以上的掌聲。
Facebook: ccClub Python讀書會
基礎線上課程:跟著商管女孩一起學 Python