本文 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,容易造成後續錯誤。
  • 支援 keyreverse 參數:
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,不會改變傳入的可迭代物件。
  • 同樣支援 keyreverse
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() 例如 intNone 會拋出 TypeError 確認傳入的是可迭代物件(list、tuple、dict、set、generator)

最佳實踐

  1. 先決定是否需要保留原資料:保留 → sorted();不保留 → list.sort()
  2. 使用 key 代替自訂比較函式key 更快且語意清晰。
  3. 對大型清單使用原位排序:可減少記憶體佔用。
  4. 在多層排序時利用穩定性:先排序次要鍵,再排序主要鍵。
  5. 避免在同一行混用兩者:如 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 結構排序的首選。
  • 兩者皆支援 keyreverse,且底層皆使用 Timsort(O(n log n)),具備 穩定性,可利用多層排序的技巧。
  • 在實務開發中,先判斷「是否需要保留原始資料」再決定使用哪個函式,能避免不必要的記憶體開銷與程式錯誤。

掌握 sort()sorted() 的差異與最佳使用時機,將讓你的 Python 程式更簡潔高效,也更符合專業開發的最佳實踐。祝你在資料結構的旅程中,排序無礙、程式順手!