本文 AI 產出,尚未審核

Python 資料結構 – 字典(dict)

主題:合併字典(| 運算子、update


簡介

在日常開發中,我們常會把資料切分成多個 字典 再分別處理,最後需要把它們重新組合成一個完整的結構。
字典的合併不僅是把鍵值對「黏」在一起,更牽涉到衝突鍵的處理、原始資料的保留與可讀性等議題。

Python 3.9 之後引入了 |(或稱「合併運算子」),讓合併行為變得直觀且不會改變原本的字典;而在較早的版本仍可使用 dict.update() 完成相同任務。
掌握這兩種寫法,能讓程式碼更簡潔、錯誤更少,也更符合「資料不可變」的設計原則。


核心概念

1. | 合併運算子(Python 3.9+)

左側字典 右側字典 結果
{'a': 1, 'b': 2} {'b': 3, 'c': 4} {'a': 1, 'b': 3, 'c': 4}
  • 左側字典 的鍵值會先被放入結果,之後 右側字典 若有相同鍵,會 覆寫 左側的值。
  • 原始兩個字典 皆不會被改變(淺拷貝的行為),因此可安全在函式中使用而不必擔心副作用。

範例 1:基本合併

dict_a = {"name": "Alice", "age": 25}
dict_b = {"age": 26, "city": "Taipei"}

merged = dict_a | dict_b
print(merged)   # {'name': 'Alice', 'age': 26, 'city': 'Taipei'}

註解age 在右側字典出現,覆寫左側的 25,形成 26。

範例 2:多個字典一次合併(利用展開運算子)

dict_c = {"country": "TW", "zip": "100"}
merged_all = dict_a | dict_b | dict_c
print(merged_all)
# {'name': 'Alice', 'age': 26, 'city': 'Taipei', 'country': 'TW', 'zip': '100'}

註解| 可鏈式使用,順序仍決定衝突鍵的最終值。

範例 3:在函式內返回新字典

def combine(*dicts):
    result = {}
    for d in dicts:
        result = result | d   # 每次產生新 dict,保持不變性
    return result

print(combine(dict_a, dict_b, dict_c))

註解:此寫法保證傳入的每個字典不會被改寫,適合純函式式編程。

2. dict.update()(所有 Python 版本)

update()直接修改 呼叫它的字典,將參數字典的鍵值寫入(衝突時覆寫)。

範例 4:使用 update 合併

dict_a = {"name": "Bob", "hobby": "reading"}
dict_b = {"hobby": "cycling", "city": "Kaohsiung"}

dict_a.update(dict_b)   # dict_a 被改寫
print(dict_a)
# {'name': 'Bob', 'hobby': 'cycling', 'city': 'Kaohsiung'}

註解dict_a 本身被改變,若需要保留原始資料,必須先 copy()

範例 5:避免副作用的安全寫法

import copy

dict_a = {"x": 1, "y": 2}
dict_b = {"y": 3, "z": 4}

merged = copy.deepcopy(dict_a)   # 先深拷貝
merged.update(dict_b)
print(merged)      # {'x': 1, 'y': 3, 'z': 4}
print(dict_a)      # {'x': 1, 'y': 2}  # 原始未被改動

註解:若字典內部還有可變物件(如 list、dict),使用 deepcopy 才能完整避免副作用。

3. 合併時的衝突策略

方法 衝突時的預設行為 可自訂策略
` ` 右側字典覆寫左側
update 參數字典覆寫原字典 同上,可在 update 前先做預處理

若想保留 兩個值(例如把衝突鍵的值變成列表),可以先使用 collections.defaultdict(list) 或自行迭代。

範例 6:保留衝突鍵的所有值

from collections import defaultdict

def merge_keep_all(*dicts):
    result = defaultdict(list)
    for d in dicts:
        for k, v in d.items():
            result[k].append(v)
    return dict(result)

a = {"id": 1, "tag": "python"}
b = {"id": 2, "tag": "coding"}

print(merge_keep_all(a, b))
# {'id': [1, 2], 'tag': ['python', 'coding']}

常見陷阱與最佳實踐

  1. 版本相容性
    • | 只能在 Python 3.9+ 使用;在較舊環境請改用 updatedict(**d1, **d2)
  2. 不小心改變原始字典
    • update 直接改寫,若程式中仍需使用原始資料,應先 copy()
  3. 深層結構的合併
    • 只合併表層鍵值,內部的可變物件(list、dict)仍是同一個參考。
    • 需要 深拷貝copy.deepcopy)或自行遞迴合併。
  4. 鍵衝突的意圖不明
    • 合併前應檢查 set(dict_a) & set(dict_b),確認哪些鍵會被覆寫。
  5. 效能考量
    • 多次使用 | 會產生多個臨時字典,若合併大量字典,使用 update(一次性)會較快。

最佳實踐

  • 不需要保留原始字典,且要合併大量資料,使用 dict_a.update(dict_b) 會較省記憶體。
  • 追求不可變性,或在函式式程式設計中避免副作用,使用 |copy().update()
  • 合併前先 列印或 log 衝突鍵,確保覆寫行為符合預期。

實際應用場景

場景 為什麼需要合併字典 建議寫法
API 回傳分段 後端分批回傳 JSON,前端需要一次性組合成完整資料 `full = part1
設定檔層疊 預設設定 + 使用者自訂設定 → 使用者設定覆寫預設 config = default_cfg.copy(); config.update(user_cfg)
資料清理 多個來源的資料欄位需要統一到同一個字典 使用 merge_keep_all 以保留所有來源的值,再後續去重
快取合併 多個快取層(memory、disk)各自保存部份資料 `combined = memory_cache
測試資料生成 測試時常會有基礎資料 + 個別測試案例的差異 `base = {...}; case = {...}; test_input = base

總結

合併字典是 Python 程式設計 中常見且實用的技巧。

  • | 運算子:語法簡潔、保持原始字典不變,適合 Python 3.9 以上的現代程式碼。
  • dict.update():兼容所有版本,但會直接修改目標字典,使用時要注意副作用。

在實務開發時,先確認 Python 版本資料是否需要保留原始狀態,再選擇最合適的合併方式;同時留意衝突鍵、深層結構與效能,才能寫出 可讀、可靠且易於維護 的程式。

祝你在 Python 的字典操作上玩得更順手,也能把資料結構的力量發揮到最大!