第 14 課 · 入門 · 19 分鐘

Python 排序與 key 函式

按照真正關心的欄位排序,而不是只對一串數字排序。

sorted 與 sortkey 函式zip 與 enumerate

訂單該按付款順序離開櫃檯,加急的除外。按一個欄位排序不難,按兩個欄位就要用到元組了。哪個欄位該排在前面,為什麼?

排序本身不難,難的是記錄有多個欄位時該按什麼排。Python 允許傳入 key 函式,把每個元素轉換成用於比較的值。這樣既保留原記錄,也讓排序規則變得明確。 你還會用穩定排序疊加規則、用元組鍵混合升降序,並用 min/max 代替只為取一個值的排序。

sorted 回傳新列表

sorted(items) 會建立新的排序列表,原列表不變。items.sort() 原地排序並回傳 None。還需要保留原順序時,用 sorted。

prices = [6.0, 4.5, 3.25]
print(sorted(prices))
print(prices)

新的排序列表

用 key 排序記錄

key 接收一個元素,回傳 Python 應該比較的值。lambda 適合寫很短的規則。這裡的第二個欄位 price 就是排序鍵。

orders = [("latte", 4.5), ("tea", 3.0), ("mocha", 5.0)]
for name, price in sorted(orders, key=lambda item: item[1]):
    print(name, price)

按價格排序元組

enumerate 與 zip

enumerate 同時給出索引和值。zip 把兩個序列的元素配對。需要位置或並行資料時,它們能讓迴圈保持清楚。

names = ["latte", "tea"]
prices = [4.5, 3.0]
for i, (name, price) in enumerate(zip(names, prices), 1):
    print(i, name, price)

索引與配對資料

穩定排序可以疊加規則

Python 的排序是穩定的:鍵相同的元素保持之前的相對順序。先按次要規則排序,再按主要規則排序;第二次排序遇到相同主鍵時,會保留第一次的順序。

orders = [
    {"name": "tea", "price": 3.0},
    {"name": "latte", "price": 4.5},
    {"name": "mocha", "price": 4.5},
]
by_name = sorted(orders, key=lambda order: order["name"])
by_price = sorted(by_name, key=lambda order: order["price"], reverse=True)
for order in by_price:
    print(order["name"], order["price"])

先次鍵,後主鍵

元組鍵混合欄位,不混合方向

元組鍵從左到右比較。(-price, name) 讓價格降序、名稱升序,因為負號只反轉了價格這個欄位。reverse=True 會把兩個欄位一起反轉,通常不是想要的結果。

orders = [("latte", 4.5), ("tea", 3.0), ("mocha", 4.5)]
for name, price in sorted(orders, key=lambda item: (-item[1], item[0])):
    print(name, price)

用計算出的鍵只反轉一個欄位

只取一個記錄時不必排序

只需要最便宜的一項時,min() 配 key 比完整排序更清楚,也更省。max() 同理。key 裡的規則要和排序時使用的規則完全一致。

orders = [("latte", 4.5), ("tea", 3.0), ("mocha", 5.0)]
cheapest = min(orders, key=lambda item: item[1])
priciest = max(orders, key=lambda item: item[1])
print(cheapest)
print(priciest)

取極值不需要完整順序

本課要點

  • sorted 回傳新列表,sort 原地修改列表。
  • key 決定按哪個值比較。
  • reverse=True 從大到小排序。
  • enumerate 加索引,zip 並行走訪兩個序列。
  • 穩定排序可以組合;元組鍵能只反轉一個欄位。

常見錯誤

  • 把 sort() 的回傳值賦回列表,結果得到 None。
  • 呼叫 key 函式而不是把函式本身傳進去。
  • 以為 zip 會在較長序列結束後繼續。
  • 用了 reverse=True,卻期待次要欄位仍然升序。
輪到你

按價格排序三個 (name, price) 元組,再列印每個名稱和價格。

動手試試

改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。

瀏覽器內 Python本機執行 · 無需帳號
首次執行會從 CDN 下載 Pyodide。修改後點擊「執行程式碼」。

練習本課

12 道題,作答後立即看到解釋。

查看 12 道題答案開始練習
← 推導式類與物件 →