Python 進階資料結構:collections 模組完全攻略 (第 11 章)

站主自己的課程,請大家支持
揭秘站長的架站心法:如何利用 Hugo × AI 打造高質感個人品牌網站? 揭秘站長的架站心法:如何利用 Hugo × AI 打造高質感個人品牌網站?
  • Post by
  • Dec 25, 2023
post-thumb

在第 4 章我們學過了 Python 的基礎四大天王:list, tuple, set, dict。但在處理特定問題時,這些通用容器可能不夠高效或不夠方便。

本章將帶您探索 Python 標準函式庫中的 collections 模組,這裡面藏著許多讓資深開發者愛不釋手的「特種部隊」。

1. Counter:計數神器

統計列表中元素出現的次數是超常見的需求。

傳統寫法 vs Counter

data = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']

# 傳統寫法
count = {}
for item in data:
    if item in count:
        count[item] += 1
    else:
        count[item] = 1
# 結果: {'apple': 3, 'banana': 2, 'orange': 1}

# 使用 Counter
from collections import Counter
c = Counter(data)
print(c)
# 結果: Counter({'apple': 3, 'banana': 2, 'orange': 1})

Counter 不僅寫法簡潔,還提供了強大的方法:

# 取得出現頻率最高的前 n 名
print(c.most_common(2))  
# [('apple', 3), ('banana', 2)]

# 兩個 Counter 相加
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2)  
# Counter({'a': 4, 'b': 3})

2. defaultdict:告別 KeyError

使用普通字典 (dict) 時,存取不存在的 key 會噴出 KeyError。我們常需要用 if key in dictdict.get() 來防禦。defaultdict 讓我們能預設一個工廠函數,當 key 不存在時自動產生預設值。

from collections import defaultdict

# 範例:將單字按首字母分組
words = ['apple', 'banana', 'cherry', 'apricot', 'blueberry']

# 設定 default_factory 為 list,不存在的 key 會自動初始化為 []
grouped_words = defaultdict(list)

for word in words:
    first_letter = word[0]
    grouped_words[first_letter].append(word)

print(dict(grouped_words))
# {'a': ['apple', 'apricot'], 'b': ['banana', 'blueberry'], 'c': ['cherry']}

常見的 factory 對應:

  • defaultdict(int) → 預設為 0 (適合計數)
  • defaultdict(list) → 預設為 [] (適合分組)
  • defaultdict(set) → 預設為 set() (適合去重分組)

3. deque:雙端佇列 (Double-ended Queue)

普通的 list 在尾端新增 (append) 或刪除 (pop) 很快 (O(1)),但在開頭插入或刪除 (insert(0, ...), pop(0)) 非常慢 (O(n)),因為所有後面的元素都要移動位置。

deque (發音如 “deck”) 專為兩端的高效操作而設計。

from collections import deque
import time

# 比較 deque 與 list 在頭部插入的效能
d = deque()
start = time.time()
for i in range(100000):
    d.appendleft(i)  # O(1) 操作
print(f"Deque 花費時間: {time.time() - start:.4f} 秒")

l = []
start = time.time()
for i in range(100000):
    l.insert(0, i)   # O(n) 操作,隨著列表變長會越來越慢
print(f"List 花費時間: {time.time() - start:.4f} 秒")

結論:如果你需要實作 Queue (先進先出)Stack (後進先出),且涉及大量的頭尾操作,請務必使用 deque

4. namedtuple:具名元組,讓程式碼更可讀

Tuple ((10, 20)) 輕量且不可變,但缺點是存取時只能用索引 (p[0], p[1]),可讀性差。namedtuple 讓我們能給每個欄位取名字,同時保留 tuple 的省記憶體特性。

from collections import namedtuple

# 定義一個 Point 結構
Point = namedtuple('Point', ['x', 'y'])

p = Point(10, 20)

# 使用屬性名稱存取,比 p[0] 清楚多了!
print(f"X座標: {p.x}, Y座標: {p.y}")

# 依然保有 tuple 的特性 (解構、不可變)
x, y = p
print(x, y)

在 Python 3.7+,我們也推薦使用 Dataclasses (下一章會提到),但在簡單的資料容器場景下,namedtuple 依然是不可多得的神器。

5. 總結

掌握 collections 模組是 Python 進階之路的必經之地。

資料結構適用情境相比基礎型別優勢
Counter統計元素頻率比手寫 for loop 簡潔且快
defaultdict分組、計數、嵌套字典避免 KeyError,省去初始化檢查
deque佇列 (Queue/Stack)頭尾操作效能遠勝 list
namedtuple簡單的資料物件比 tuple 具可讀性,比 class 省記憶體

下一章,我們將探討 Python 中最迷人的特性之一:迭代器 (Iterators) 與生成器 (Generators),學會如何處理無限大的數據流!


延伸閱讀

LATEST POST
TAG