專項練習 · 9 分鐘閱讀

Python itertools 練習指南:惰性資料流

itertools 是一組小型迭代器工具。它們不試圖解決整個問題,而是可以組合。認識 count、islice、chain 和 groupby 之後,很多迴圈會變得更短、更容易讀。

給無限流加上邊界

count 會永遠產生值,只有另一個函式限制請求數量時才有用。islice 截取前綴,而不把整個流變成列表。結果仍然是惰性的,需要查看時再包起來。

from itertools import count, islice
print(list(islice(count(10), 3)))

有限的視窗

用 chain 拼接資料流

chain 按順序讀取輸入,不會排序或去重。它可以直接替代「把一個列表追加到另一個列表」的迴圈。輸入很大時,chain 不必預先建構合併後的列表。

from itertools import chain
print(list(chain([1, 2], ["a", "b"])))

兩個流合成一個

只分組相鄰的鍵

groupby 在鍵變化時開始新組,不會搜尋整個輸入中的相同鍵。相同值不相鄰時,先按同一個鍵排序再分組。前進到下一組之前,要消費完目前分組。

from itertools import groupby
data = [("a", 1), ("a", 2), ("b", 3)]
for key, group in groupby(data, key=lambda pair: pair[0]):
    print(key, list(group))

連續分組

先排序再分組

分組鍵和排序鍵必須一致。穩定排序會保留相同鍵內部的原始順序,這通常正是想要的結果。排序後,groupby 才能把相同鍵看成一個連續片段。

from itertools import groupby
data = [("b", 1), ("a", 2), ("a", 3)]
sorted_data = sorted(data, key=lambda pair: pair[0])
print([(key, len(list(group))) for key, group in groupby(sorted_data, key=lambda pair: pair[0])])

先排序,再分組

有意識地物化

大多數 itertools 結果是迭代器,只能消費一次,也不支援下下標存取。需要列印、計數或多輪走訪時,再轉換成列表。物化應發生在邊界,而不是每個階段中間。

from itertools import chain
result = list(chain([1], [2, 3]))
print(result)
print(result[1])

在邊界轉成列表

你的練習清單

常見問題

groupby 會排序資料嗎?

不會。它只分組相鄰元素。相同鍵可能分散時,先按同一個鍵排序。

chain 可以拼接字串嗎?

可以。它會像 for 迴圈一樣,逐個字元走訪每個字串。

為什麼 itertools 結果是空的?

很可能迭代器已經被消費。迭代器只能使用一次;建立新的迭代器,或者先物化成列表。

練習 itertools

12 道題,覆蓋 count、islice、chain、groupby 和惰性迭代器行為。

打開練習

繼續閱讀: