本文 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']}
常見陷阱與最佳實踐
- 版本相容性
|只能在 Python 3.9+ 使用;在較舊環境請改用update或dict(**d1, **d2)。
- 不小心改變原始字典
update直接改寫,若程式中仍需使用原始資料,應先copy()。
- 深層結構的合併
- 只合併表層鍵值,內部的可變物件(list、dict)仍是同一個參考。
- 需要 深拷貝(
copy.deepcopy)或自行遞迴合併。
- 鍵衝突的意圖不明
- 合併前應檢查
set(dict_a) & set(dict_b),確認哪些鍵會被覆寫。
- 合併前應檢查
- 效能考量
- 多次使用
|會產生多個臨時字典,若合併大量字典,使用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 的字典操作上玩得更順手,也能把資料結構的力量發揮到最大!