本文 AI 產出,尚未審核

Python – 資料結構 – 清單(list)

主題:enumerate / zip 搭配使用


簡介

在日常的 Python 程式中,我們常常需要同時取得序列的索引與元素,或是把多個序列「配對」起來進行迭代。
enumerate()zip() 正是為了這兩類需求而設計的內建函式。單獨使用時已相當便利,結合兩者則能讓程式碼更簡潔、可讀性更高,且避免手動管理索引或使用笨重的 for i in range(len(...)) 迴圈。

本篇文章將從概念說明、實作範例、常見陷阱到實務應用,完整介紹 enumeratezip 的搭配技巧,幫助初學者快速上手,也讓中階開發者在大型專案中能寫出更具可維護性的程式。


核心概念

1. enumerate() 的基本用法

fruits = ["apple", "banana", "cherry"]
for idx, fruit in enumerate(fruits):
    print(f"{idx}: {fruit}")
  • enumerate(iterable, start=0) 會把 可疊代物件 轉成 (index, element) 的二元組。
  • start 參數允許自訂起始索引,常用於 1‑based 編號情境。

2. zip() 的基本用法

names = ["Alice", "Bob", "Charlie"]
ages  = [25, 30, 22]

for name, age in zip(names, ages):
    print(f"{name} is {age} years old")
  • zip(*iterables) 會把多個可疊代物件「平行」配對,產生 tuple 序列。
  • 配對的長度以最短的可疊代物件為基準,避免 IndexError。

3. 為什麼要 同時 使用 enumeratezip

在以下情境中,僅靠 enumeratezip 任一個函式都無法完整表達需求:

  • 同時取得索引與多個序列的元素(如同時遍歷兩個平行清單,且需要知道目前的第幾筆資料)。
  • 以索引為鍵,將多個序列「合併」成字典,或是 產生矩陣的轉置

下面的範例將示範這些實際需求,並說明如何正確、有效率地結合兩者。


程式碼範例

範例 1:同時遍歷索引與多個清單

students = ["Anna", "Ben", "Cindy"]
scores_a = [88, 92, 79]
scores_b = [90, 85, 95]

for idx, (student, a, b) in enumerate(zip(students, scores_a, scores_b), start=1):
    total = a + b
    print(f"{idx}. {student} – A: {a}, B: {b}, 總分: {total}")
  • zip(students, scores_a, scores_b) 產生 (student, a, b) 三元組。
  • enumerate(..., start=1) 再把每筆資料編號為 1 起始
  • 這樣寫比 for i in range(len(students)):安全,不會因長度不一致而產生錯誤。

範例 2:利用 enumerate + zip 建立字典(索引 → 多欄位資料)

keys   = ["id", "name", "age"]
values = [
    [101, "Tom", 28],
    [102, "Lucy", 24],
    [103, "Mike", 31],
]

# 產生 {0: {"id":101, "name":"Tom", "age":28}, ...}
result = {
    idx: dict(zip(keys, row))
    for idx, row in enumerate(values)
}
print(result)
  • zip(keys, row) 把欄位名稱與對應值配對,產生字典。
  • enumerate(values) 為每筆資料加上唯一索引(可用於資料庫主鍵或序列化)。

範例 3:交錯處理兩個清單(奇偶索引分別處理)

list1 = ["a", "b", "c", "d"]
list2 = [1, 2, 3, 4]

for idx, (ch, num) in enumerate(zip(list1, list2)):
    if idx % 2 == 0:          # 偶數索引
        print(f"偶索引 {idx}: {ch}{num}")
    else:                     # 奇數索引
        print(f"奇索引 {idx}: {num}{ch}")
  • 透過 enumerate 取得 索引,再用條件分支決定不同的處理方式。
  • 這在 資料清理交錯合併(interleaving)時非常實用。

範例 4:使用 itertools.zip_longest 處理長度不等的清單

from itertools import zip_longest

names = ["Alice", "Bob"]
scores = [95, 88, 76]          # 多出一筆

for idx, (name, score) in enumerate(zip_longest(names, scores, fillvalue="N/A")):
    print(f"{idx}: {name} -> {score}")
  • zip_longest 會以最長的序列為基準,缺少的元素以 fillvalue 填補。
  • 結合 enumerate 後,仍能取得正確的 索引

範例 5:矩陣轉置(list of lists → zip + enumerate)

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

# 轉置後的矩陣
transpose = [list(row) for row in zip(*matrix)]

for idx, row in enumerate(transpose):
    print(f"第 {idx+1} 列: {row}")
  • zip(*matrix) 會把每一列的相同位置元素「配對」成新列,完成 轉置
  • enumerate 用來顯示每一列的編號,方便檢查結果。

常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記 zip 只會配對到最短序列 若兩個清單長度不同,較長的部份會被「截斷」而遺失資料。 使用 itertools.zip_longest,或在迴圈前先檢查長度。
enumerate 中直接改變原序列 直接在迴圈內 list.append() 可能導致 RuntimeError(序列在迭代時改變)。 若需要產生新資料,先使用 list() 產生副本或使用 for idx, item in enumerate(original[:]):
enumerate 放在 zip 內部 enumerate(zip(...)) 只會給 配對後的 tuple 編號,無法取得單獨的索引與元素。 正確順序應為 enumerate(zip(...))(可行),但若需要 索引與多個元素,建議使用 for idx, (a, b) in enumerate(zip(...)):
使用 enumerate 時忘記 start 參數 常見於報表或 UI 顯示,需要從 1 開始編號。 明確寫 enumerate(iterable, start=1),避免後續手動 +1
過度嵌套 zip 多層 zip(zip(...), ...) 會產生難以閱讀的 tuple,降低可維護性。 盡量使用一次 zip(*iterables),或提前將資料整理成 list of tuples 再迭代。

最佳實踐

  1. 保持可讀性:即使可以寫成一行 for i, (a, b) in enumerate(zip(...)):,若條件較複雜,建議拆成兩行或加上註解。

  2. 使用型別提示(Python 3.5+)提升 IDE 補全與靜態檢查:

    from typing import List, Tuple
    
    def pair_scores(names: List[str], scores: List[int]) -> List[Tuple[int, str, int]]:
        return [(i, n, s) for i, (n, s) in enumerate(zip(names, scores))]
    
  3. 盡量避免手動 range(len(...))enumerate 自帶索引,可免除越界錯誤。

  4. 在大量資料處理時,若需要 記憶體友好 的迭代,請直接使用 enumerate(zip(...))(產生 generator),而非先把 list(zip(...)) 轉成列表。


實際應用場景

場景 為什麼需要 enumerate + zip 範例概念
CSV 檔案資料清理 同時取得行號與多欄位值,快速定位錯誤行。 for line_no, (id_, name, score) in enumerate(zip(col_id, col_name, col_score), start=2):
圖形化繪圖(matplotlib) 把多條曲線的座標與顏色、標籤配對,同時編號產生圖例。 for idx, (x, y, color) in enumerate(zip(xs, ys, colors)):
機器學習特徵工程 把特徵名稱與對應的向量索引對應起來,產生字典供模型使用。 feature_dict = {idx: name for idx, name in enumerate(feature_names)}
網路爬蟲分批請求 同時遍歷 URL 清單與對應的請求參數,並以序號作為日誌編號。 for idx, (url, payload) in enumerate(zip(urls, params), start=1):
多語系訊息對照表 把語系代碼與訊息文字配對,同時保留行號以便於翻譯工作流程。 for idx, (lang, msg) in enumerate(zip(langs, messages), start=1):

以上情境皆顯示,enumerate + zip 能在保持程式簡潔的同時,提供索引資訊多序列同步迭代的能力,對於資料處理、報表產出、測試腳本等都有顯著效益。


總結

  • enumerate() 為可疊代物件自動產生 (index, element)zip() 則把多個序列「平行」配對。
  • 兩者結合後,可一次取得 索引多個元素,讓程式碼避免使用 range(len(...)),減少 IndexError維護成本
  • 常見的陷阱包括 長度不一致迭代時修改原序列、以及 過度嵌套,只要遵循最佳實踐(保持可讀性、使用 start、適時使用 zip_longest),即可寫出安全且易於除錯的程式。
  • 在實務上,這組技巧廣泛應用於 資料清理、報表產出、圖形繪製、機器學習前處理 等領域,幾乎是每位 Python 開發者的必備功力。

掌握 enumeratezip 的搭配使用,等於在 Python 的工具箱裡多了一把「雙頭螺絲刀」——既能快速定位問題,又能同時處理多筆資料,讓你的程式寫得更簡潔可靠易讀。祝你在日常開發中玩得開心,寫出更優雅的 Python 程式!