本文 AI 產出,尚未審核
Python 資料結構 – 清單(list)
主題:sort() vs sorted()
簡介
在日常的 Python 開發中,**清單(list)**是最常使用的可變序列型別。無論是資料前處理、演算法實作,或是 UI 顯示,都離不開對清單的排序操作。Python 提供了兩個看似相似、卻有本質差異的排序工具:list.sort() 方法與內建函式 sorted()。
許多初學者在學習時會把兩者混為一談,導致程式行為不如預期或是效能受損。本篇文章將深入比較 sort() 與 sorted(),說明它們的使用時機、返回值、原地修改與副本產生的差別,並提供實務範例、常見陷阱與最佳實踐,協助你在寫程式時做出正確的選擇。
核心概念
1. 基本差異概覽
| 特性 | list.sort() |
sorted() |
|---|---|---|
| 是否返回值 | 無(返回 None) |
回傳 新清單 |
| 是否改變原清單 | 是(原位排序) | 否(保留原清單) |
| 適用對象 | 只能作用於 list 物件 | 可接受 任何可迭代物件(list、tuple、dict、set、generator) |
| 可指定 key / reverse | ✅ | ✅ |
| 時間複雜度 | O(n log n)(Timsort) | 同上,額外產生一個副本的空間 O(n) |
重點:若你需要保留原始資料,或是想對非 list 的可迭代物件排序,請使用
sorted();若你只想就地排序且不需要副本,list.sort()是更省記憶體的選擇。
2. list.sort() 的使用方式
numbers = [5, 2, 9, 1, 5, 6]
numbers.sort() # 原位排序
print(numbers) # [1, 2, 5, 5, 6, 9]
- 返回值為
None,若不小心寫成sorted_numbers = numbers.sort(),sorted_numbers會得到None,容易造成後續錯誤。 - 支援
key與reverse參數:
words = ['apple', 'Banana', 'cherry', 'date']
words.sort(key=str.lower) # 忽略大小寫排序
words.sort(key=len, reverse=True) # 依長度由長到短
print(words) # ['Banana', 'cherry', 'apple', 'date']
3. sorted() 的使用方式
fruits = ('orange', 'apple', 'banana')
sorted_fruits = sorted(fruits) # 產生新清單,不改變原 tuple
print(sorted_fruits) # ['apple', 'banana', 'orange']
print(fruits) # ('orange', 'apple', 'banana')
- 返回一個全新的 list,不會改變傳入的可迭代物件。
- 同樣支援
key、reverse:
students = [
{'name': 'Alice', 'score': 85},
{'name': 'Bob', 'score': 92},
{'name': 'Cathy', 'score': 78},
]
# 依成績由高到低排序
sorted_students = sorted(students, key=lambda s: s['score'], reverse=True)
print(sorted_students)
4. 何時選擇 sorted() 而非 list.sort()
a. 需要保留原始資料
original = [3, 1, 4, 1, 5]
# 只想看排序後的結果,不想改變 original
view = sorted(original)
print(original) # [3, 1, 4, 1, 5]
print(view) # [1, 1, 3, 4, 5]
b. 排序非 list 的資料型別
# 排序字典的鍵
my_dict = {'b': 2, 'a': 1, 'c': 3}
sorted_keys = sorted(my_dict) # ['a', 'b', 'c']
sorted_items = sorted(my_dict.items(), key=lambda kv: kv[1]) # 依值排序
c. 結合生成器(generator)使用
gen = (x * x for x in range(10)) # 產生 0~81 的平方
sorted_gen = sorted(gen) # 必須先把 generator 轉成 list 再排序
print(sorted_gen) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
5. 進階範例:多層排序(stable sort)
Python 的排序演算法是 穩定(stable) 的,意即相等的元素會保留原本的相對順序。利用這一特性,我們可以先依次排序不同的鍵:
people = [
{'name': 'Alice', 'age': 30, 'salary': 7000},
{'name': 'Bob', 'age': 25, 'salary': 5000},
{'name': 'Cathy', 'age': 30, 'salary': 6000},
{'name': 'David', 'age': 25, 'salary': 8000},
]
# 先依 salary 升冪,接著依 age 升冪(因為 sort 是穩定的)
people.sort(key=lambda p: p['salary'])
people.sort(key=lambda p: p['age'])
print(people)
結果會先把同齡的人依 salary 排序,最後的 people 會是:
[
{'name': 'Bob', 'age': 25, 'salary': 5000},
{'name': 'David', 'age': 25, 'salary': 8000},
{'name': 'Cathy', 'age': 30, 'salary': 6000},
{'name': 'Alice', 'age': 30, 'salary': 7000},
]
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
把 list.sort() 的回傳值當作排序結果 |
sorted_list = my_list.sort() 會得到 None |
直接使用 my_list.sort(),或改用 sorted(my_list) |
| 在迭代中同時修改原清單 | 迭代時呼叫 list.sort() 可能導致迭代順序改變 |
先把清單複製或使用 sorted() 產生新清單 |
忘記 key 參數的作用 |
想要依自訂規則排序卻只傳 reverse=True |
使用 key 搭配 lambda 或函式,例如 key=str.lower |
對大型資料使用 sorted() 產生大量副本 |
會佔用額外 O(n) 記憶體,導致效能下降 | 若不需要保留原始資料,改用 list.sort();若必須保留,考慮分批排序或外部排序演算法 |
對不可迭代物件使用 sorted() |
例如 int、None 會拋出 TypeError |
確認傳入的是可迭代物件(list、tuple、dict、set、generator) |
最佳實踐
- 先決定是否需要保留原資料:保留 →
sorted();不保留 →list.sort()。 - 使用
key代替自訂比較函式:key更快且語意清晰。 - 對大型清單使用原位排序:可減少記憶體佔用。
- 在多層排序時利用穩定性:先排序次要鍵,再排序主要鍵。
- 避免在同一行混用兩者:如
sorted(my_list.sort())沒有意義且會產生錯誤。
實際應用場景
1. 網站後端:分頁顯示排序結果
def get_paginated_users(page: int, per_page: int, sort_by: str = 'username'):
# 從資料庫取得全部使用者(此處簡化為 list)
users = fetch_all_users() # 回傳 list of dict
# 根據使用者需求排序
sorted_users = sorted(users, key=lambda u: u[sort_by])
start = (page - 1) * per_page
end = start + per_page
return sorted_users[start:end]
使用
sorted()讓原始users列表保持不變,方便其他模組再次使用。
2. 資料分析:對 DataFrame 之外的清單排序
prices = [12.5, 9.99, 15.0, 7.5, 9.99]
# 只想取前 3 個最低價格
lowest_three = sorted(prices)[:3] # [7.5, 9.99, 9.99]
3. CLI 工具:即時顯示排序後的檔案列表
import os
files = os.listdir('.')
# 依檔案大小排序(由大到小)
files.sort(key=lambda f: os.path.getsize(f), reverse=True)
for f in files:
print(f, os.path.getsize(f))
這裡使用
list.sort(),因為不需要保留原始檔名順序,且直接在原位排序可以減少 I/O 時間。
4. 教學或測試:比較兩種排序結果
data = [3, 1, 4, 1, 5, 9, 2]
copy = data[:] # 產生副本
data.sort()
sorted_copy = sorted(copy)
assert data == sorted_copy # 兩者結果相同
總結
list.sort()原位排序、返回None、只適用於 list,適合需要省記憶體且不保留原始順序的情境。sorted()返回新 list、不改變來源、接受 任何可迭代物件,是保留原資料或對非 list 結構排序的首選。- 兩者皆支援
key與reverse,且底層皆使用 Timsort(O(n log n)),具備 穩定性,可利用多層排序的技巧。 - 在實務開發中,先判斷「是否需要保留原始資料」再決定使用哪個函式,能避免不必要的記憶體開銷與程式錯誤。
掌握 sort() 與 sorted() 的差異與最佳使用時機,將讓你的 Python 程式更簡潔、高效,也更符合專業開發的最佳實踐。祝你在資料結構的旅程中,排序無礙、程式順手!