第 21 課 · 進階 · 18 分鐘

Python enumerate 與 zip

不用手寫計數器,也能拿到位置並並行走訪序列。

enumeratezip迴圈索引並行列表

小票要行號,價目表要把商品和價格配成對。兩個都是迴圈,裡面都藏著一個計數器。哪個內建函式能把這份記帳活接過去?

兩個小工具能省掉大量迴圈裡的雜活。enumerate 會給每個元素一個位置,zip 則讓兩個或多個序列並排前進。它們回傳的都是迭代器,想直接查看結果時記得用 list() 包起來。 你還會發現 zip 的靜默截斷、用 enumerate 走訪惰性迭代器,並用 zip(*rows) 轉置行。

不用手寫計數器

enumerate(colors) 每輪產生一對值:索引和元素。直接在 for 語句裡解包這一對即可。它比單獨維護一個計數器再手動加一更清楚。

colors = ["red", "green", "blue"]
for index, color in enumerate(colors):
    print(index, color)

索引和元素一起拿到

從 1 開始編號

start 參數只改變第一個編號,不會改變原序列。面向人的步驟或排行榜可以從 start=1 開始。原列表本身不會被修改。

for number, color in enumerate(["red", "green"], start=1):
    print(f"{number}: {color}")

更適合展示的編號

並行走訪兩個列表

zip 會在最短的輸入結束時停止。資料本來就該一一對應時這很方便,但長度不一致時也可能掩蓋缺失值。需要計數或把配對當成元組列印時,可以先轉成列表。

names = ["Ada", "Grace"]
scores = [92, 88]
for name, score in zip(names, scores):
    print(f"{name}: {score}")
print(list(zip([1, 2], ["a", "b"])))

並行序列

zip 會靜默停止,strict 會報錯

zip 在最短輸入處停止。資料本來一一對應時很方便,缺失一條記錄時卻會悄悄丟資料。strict=True 會把長度不一致變成 ValueError,而不是截斷。在兩邊本應等長的邊界上使用它。

names = ["Ada", "Grace", "Lin"]
scores = [92, 88]
print(list(zip(names, scores)))
try:
    print(list(zip(names, scores, strict=True)))
except ValueError as error:
    print(type(error).__name__, error)

靜默截斷與明確失敗

enumerate 適用於任何迭代器

enumerate 不需要 len() 或下標。它取一個元素,產出一對值,因此生成器、檔案和資料庫遊標都能用。需要位置時用它;不要為了計數先把大序列全部裝進列表。

def readings():
    yield 4.5
    yield 3.0
    yield 5.25

for position, value in enumerate(readings(), start=1):
    print(position, value)

給惰性流編號

用 zip(*rows) 轉置行

zip(*rows) 把一組行變成列。它是把名稱和價格分開的緊湊寫法,但每一行的寬度必須相同。結果是由元組組成的迭代器;需要查看完整形狀時再轉換。

rows = [("latte", 4.5), ("tea", 3.0), ("mocha", 5.0)]
names, prices = zip(*rows)
print(names)
print(prices)
print(sum(prices))

行進去,列出來

本課要點

  • enumerate 產生 (索引, 元素) 對。
  • 編號要展示給人看時,用 start=1。
  • zip 並行走訪序列,並在最短的序列處停止。
  • 這兩個函式回傳迭代器,不是列表。
  • zip 在最短輸入處靜默停止;strict=True 讓長度不一致顯式失敗。

常見錯誤

  • 不轉成列表就直接對 enumerate 的結果做下標存取。
  • 以為 zip 會用 None 補齊較短的輸入;它其實會停止。
  • enumerate 已經能表達意圖,卻仍然手寫計數器。
  • 假設輸入等長,結果丟掉了較長序列的尾部。
輪到你

給 ['Ada', 'Grace'] 列印從 1 開始的編號,再列印 list(zip([1, 2], ['a', 'b']))。

動手試試

改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。

瀏覽器內 Python本機執行 · 無需帳號
首次執行會從 CDN 下載 Pyodide。修改後點擊「執行程式碼」。

練習本課

12 道題,作答後立即看到解釋。

查看 12 道題答案開始練習
← 正規表達式pathlib 路徑 →