本文 AI 產出,尚未審核

Python 資料結構 – 字典(dict)

主題:字典推導式(Dictionary Comprehension)


簡介

在 Python 中,字典是一種以「鍵-值」配對儲存資料的容器,常用於快速查找、關聯映射等情境。隨著程式規模的擴大,我們往往需要依據既有資料產生新的字典;若採用傳統的 for 迴圈與 dict.update(),程式碼會變得冗長且不易閱讀。

字典推導式(dictionary comprehension) 是 Python 在 2.7(與 3.x)加入的語法糖,讓我們可以在一行內完成「迭代 → 條件過濾 → 產生鍵值」的整個流程。它不僅縮短程式碼,還能提升執行效能,讓資料轉換的意圖一目了然,對 初學者 了解資料流向非常有幫助,對 中級開發者 則是撰寫乾淨、易維護程式碼的利器。

本篇將從概念說明、實作範例、常見陷阱與最佳實踐,直到實務應用場景,完整呈現字典推導式的全貌,幫助你在日常開發中快速上手並避免踩雷。


核心概念

什麼是字典推導式?

字典推導式的語法結構如下:

{ <key_expr> : <value_expr> for <item> in <iterable> if <condition> }
  • <key_expr>:產生鍵的表達式,可是任意可雜湊 (hashable) 的物件。
  • <value_expr>:對應的值,可以是計算結果、函式回傳或任何 Python 表達式。
  • for <item> in <iterable>:迭代來源,常見的有 listrange()dict.items() 等。
  • if <condition>(可選):條件過濾,只保留滿足條件的項目。

重點:字典推導式會一次性建立完整的字典,與使用 for 迴圈逐筆 dict[key]=value 的差別在於 一次性分配記憶體,通常較為高效。


範例一:從列表產生平方表

# 產生 0~9 的平方字典:{0: 0, 1: 1, 2: 4, ...}
squares = {x: x**2 for x in range(10)}
print(squares)

說明x 為迭代變數,x 本身作為鍵,x**2 作為值,結果是一個 鍵=原數、值=平方 的映射。


範例二:過濾奇數並轉成字串

# 只保留奇數,鍵為整數,值為字串形式
odd_str = {n: f"Number {n}" for n in range(1, 11) if n % 2 == 1}
print(odd_str)

說明if n % 2 == 1 為條件過濾,只把奇數加入字典。f"Number {n}" 使用 f-string 產生說明文字。


範例三:反轉字典(鍵值互換)

original = {"apple": 1, "banana": 2, "cherry": 3}
# 產生新字典:{1: "apple", 2: "banana", 3: "cherry"}
reversed_dict = {v: k for k, v in original.items()}
print(reversed_dict)

說明original.items() 會回傳 (key, value) 元組,利用解構直接交換位置,完成 鍵值翻轉


範例四:統計字元出現次數

sentence = "Python 程式設計教學"
# 先把字元切成清單,再用推導式計算次數
char_count = {ch: sentence.count(ch) for ch in set(sentence)}
print(char_count)

說明set(sentence) 取得不重複的字元集合,避免重複計算;sentence.count(ch) 回傳每個字元的出現次數,產生 字元頻率表


範例五:結合兩個列表產生映射(zip + dict comprehension)

keys   = ["name", "age", "city"]
values = ["Alice", 28, "Taipei"]

profile = {k: v for k, v in zip(keys, values)}
print(profile)

說明zip() 把兩個等長的可迭代物件配對,推導式直接把配對結果寫入新字典,等同於 dict(zip(keys, values)),但更具可讀性,且可加入過濾條件。


常見陷阱與最佳實踐

陷阱 可能的問題 解決方式 / 最佳實踐
鍵不可雜湊 使用列表、字典等可變物件作為鍵會拋出 TypeError 只使用不可變且可雜湊的類型(int, str, tuple 等)。
重複鍵被覆寫 若迭代來源產生重複鍵,後面的值會覆寫前面的值,可能不易察覺。 先檢查來源資料是否唯一,或使用 collections.defaultdict(list) 收集多值。
過度複雜的表達式 把太多邏輯塞進一行,讀者難以快速理解。 盡量保持單行簡潔,必要時拆成多行或使用輔助函式。
忘記 if 條件的括號 條件過濾寫錯會導致意外包含不該有的項目。 在條件較複雜時,使用括號明確分組,例如 if (a and b) or c:
記憶體使用過大 大型資料集一次性產生完整字典會佔用大量記憶體。 考慮使用生成器 ((k: v for ...)) 搭配 itertools,或改用 for 迴圈逐筆寫入檔案/資料庫。

最佳實踐

  1. 保持可讀性:即使語法允許一行寫完,也可以使用多行寫法,讓每個部份對齊,提升可讀性。
    result = {
        k: v * 2
        for k, v in source.items()
        if v % 2 == 0
    }
    
  2. 使用 dict() 直接建構:當僅需簡單映射且不需要條件過濾時,dict(zip(...)) 仍是最直觀的寫法。
  3. 善用 setdefault / defaultdict:若需要同一鍵對應多個值,先用推導式產生基礎字典,再配合 setdefaultdefaultdict 進行後續累積。
  4. 測試邊界:對空集合、單元素、重複鍵等情況寫單元測試,確保推導式行為符合預期。

實際應用場景

1. 讀取 CSV 檔並以欄位值作為鍵的索引

import csv

with open("employees.csv", newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    # 以 employee_id 為鍵,整筆資料為值
    employee_dict = {row["employee_id"]: row for row in reader}

說明:一次性把 CSV 讀成字典,之後查詢 employee_dict["E123"] 即可得到完整紀錄,效能遠高於每次遍歷列表搜尋。

2. 快速產生 API 回傳的 JSON 轉換表

# 假設有一組外部系統的代碼與本系統的代碼對應
external_codes = ["A1", "B2", "C3"]
internal_codes = [1001, 1002, 1003]

code_map = {ext: int_code for ext, int_code in zip(external_codes, internal_codes)}
# 之後在處理 API 請求時直接使用 code_map 轉換

3. 統計網站日誌的狀態碼分布

log_lines = [
    "200 OK", "404 Not Found", "200 OK",
    "500 Internal Server Error", "200 OK"
]

status_counts = {code: log_lines.count(code) for code in set(log_lines)}
print(status_counts)   # {'200 OK': 3, '404 Not Found': 1, '500 Internal Server Error': 1}

4. 用於資料清理:把字串型別的數字轉成真正的 int

raw_data = {"id": "00123", "age": "27", "score": "88"}
clean_data = {k: int(v) if v.isdigit() else v for k, v in raw_data.items()}
print(clean_data)   # {'id': 123, 'age': 27, 'score': 88}

5. 建立多層次的巢狀字典(nested dict)快速初始化

keys = ["2023", "2024", "2025"]
nested = {year: {month: [] for month in range(1, 13)} for year in keys}
print(nested["2023"][1])   # []

總結

字典推導式是 Python 中 既簡潔又高效 的資料結構操作方式。透過單行或多行的寫法,我們可以:

  • 快速產生映射(如平方表、字元頻率)
  • 結合條件過濾,只保留需要的鍵值對
  • 在資料清理、API 轉換、日誌統計 等實務情境中,大幅減少樣板程式碼

在使用時,務必注意 鍵的可雜湊性、避免重複鍵覆寫,以及 大型資料集的記憶體負擔。遵守最佳實踐、保持程式碼可讀性,才能讓字典推導式成為你日常開發的得力助手。

實務建議:在每個新功能或資料轉換任務開始前,先思考「是否可以用字典推導式一次完成?」若答案是「是」,那麼就把它寫進程式,讓程式碼更乾淨、效能更佳。祝你寫程式愉快,玩轉 Python!