早高峰的訂單像一條長河,後廚卻按十份一批做。itertools 可以切片,又不用複製整條河。哪個工具分組時不會丟掉餘數?
itertools 為迭代器流水線提供小型積木。除非你主動要求,它們不會建立完整列表,因此很適合與生成器組合。先掌握 count、islice、chain 和 groupby,再用它們組合,而不是再寫一個索引迴圈。 你還會累積總量、用 tee 複製迭代器,並構造組合。
不用 range 也能計數
count 會產生無限序列。配合 islice 只請求固定數量的值。islice 適用於任何可迭代物件,不只是列表,而且回傳迭代器。
from itertools import count, islice
print(list(islice(count(10), 3)))給無限序列加上邊界
用 chain 拼接可迭代物件
chain 會依次讀取一個可迭代物件,再讀取下一個,不建構中間列表。資料來自不同來源時很方便。只有需要列印或重複使用時,才用 list 包起來。
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))相同鍵的連續片段
accumulate 與 pairwise 派生流
accumulate 把值變成執行總量;pairwise 產出相鄰對。兩者都是惰性的,不用索引迴圈就能表達關係。累加器需要零這樣的起點時,用 initial=。
from itertools import accumulate, pairwise
values = [4, 3, 2]
print(list(accumulate(values)))
print(list(pairwise(values)))
print(list(accumulate(values, initial=0)))執行總量與相鄰對
tee 複製迭代器,但會緩衝
tee 在一個源上回傳兩個獨立迭代器。它不是深拷貝:一個分支提前取走的值會為另一個分支緩衝。兩個分支都要消費;資料小到可以直接存下時,優先用列表。
from itertools import tee
values = iter([1, 2, 3])
a, b = tee(values)
print(list(a))
print(list(b))
print(list(a))兩個讀者,一個帶緩衝的源
組合與笛卡爾積
combinations 選出不重複、無序的組合;product 構造輸入之間的所有配對。結果數量增長很快,所以輸入要小。它們適合測試矩陣和小型搜尋空間,不適合暴力列舉大資料。
from itertools import combinations, product
print(list(combinations(["a", "b", "c"], 2)))
print(list(product([1, 2], ["x", "y"])))小型組合積木
本課要點
- count 可以表示無限的等差序列。
- islice 可以從任何可迭代物件中截取有限前綴。
- chain 會把多個可迭代物件首尾相接。
- groupby 分組相鄰元素;鍵分散時要先排序。
- itertools 函式回傳迭代器,不是列表。
- accumulate 和 pairwise 派生惰性流;tee 會緩衝;組合數量增長很快。
常見錯誤
- 直接對 itertools 回傳的迭代器做下標存取。
- 在前進 groupby 之前忘記消費目前分組。
- 對未排序資料使用 groupby,卻期待全局分組。
- 對超大流使用 tee,卻以為它完全不佔記憶體。
列印 count(10) 的前三個值,再用 chain 拼接 [1, 2] 和 [3]。
動手試試
改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。
練習本課
12 道題,作答後立即看到解釋。