Python 資料結構 – 字典(dict)
主題:get() 預設值
簡介
在日常開發中,**字典(dict)**是 Python 最常使用的資料結構之一。它以鍵(key)對應值(value)的方式儲存資料,查詢速度快、語法直觀。然而,當我們嘗試從字典取值時,如果鍵不存在就會拋出 KeyError,這在大量資料處理或使用者輸入時會造成程式中斷。
dict.get() 方法正是為了解決這類問題而設計的:它允許我們在鍵不存在時回傳預設值,而不是例外。掌握這個技巧不僅能寫出更健壯的程式,也能讓程式碼更簡潔、可讀性更高。本文將深入探討 get() 的用法、常見陷阱與最佳實踐,並提供多個實務範例,協助你在 Python 開發中善用這項功能。
核心概念
1. dict.get() 的基本語法
value = my_dict.get(key, default=None)
key:欲查詢的鍵。default(可選):當鍵不存在時回傳的值,預設為None。- 回傳值:若
key在字典中,回傳對應的值;否則回傳default。
小技巧:如果只傳入
key而不提供default,等同於my_dict.get(key, None),此時仍不會拋出KeyError。
2. 為什麼不直接使用 my_dict[key]?
# 可能拋出 KeyError
value = my_dict['missing_key']
- 直接使用方括號存取會在鍵不存在時拋出例外,必須額外使用
try/except或if key in my_dict來防護。 get()讓這類防護變得一行搞定,程式碼更為簡潔。
3. default 可以是任意型別
default 不必是固定的值,任何 Python 物件皆可,例如:
- 整數、字串、列表、集合、甚至函式或自訂類別的實例。
# 預設值為空列表
items = inventory.get('apple', [])
4. default 與可變物件的注意事項
若 default 為可變物件(如列表、字典),每次呼叫 get() 都會回傳同一個物件的參考。這在需要「每次都得到全新」的情況下會產生意外的共享狀態。
# 錯誤示範:所有缺少的鍵會共用同一個列表
data = {}
data.setdefault('scores', []).append(95) # 正確做法
# 若改用 get()
data.get('scores', []).append(88) # 會拋錯,因為返回的是臨時列表
解決方案是使用 default_factory(如 collections.defaultdict)或在 get() 後自行建立新物件。
程式碼範例
下面提供 5 個實用範例,說明 get() 在不同情境下的運用方式。每個範例都附有說明註解,方便初學者快速理解。
範例 1:基本的預設值使用
# 建立一個簡單的字典
person = {'name': 'Alice', 'age': 30}
# 取出已存在的鍵
name = person.get('name') # -> 'Alice'
# 取出不存在的鍵,提供預設值
city = person.get('city', 'Taipei') # -> 'Taipei'
print(f"{name} lives in {city}.")
說明:
city不在person中,get()直接回傳'Taipei',避免了KeyError。
範例 2:使用 None 作為預設值(最常見)
# 假設從 API 取得的資料可能缺少某些欄位
api_response = {'id': 123, 'status': 'ok'}
# 直接使用 get(),若欄位缺失回傳 None
error_msg = api_response.get('error')
if error_msg is None:
print("No error, continue processing.")
else:
print(f"Error: {error_msg}")
技巧:
None常被用來表示「未提供」或「無值」的情況,配合if value is None判斷即可。
範例 3:預設值為可變物件(列表)
# 記錄每個使用者的購物車內容
carts = {
'alice': ['book', 'pen'],
# bob 尚未有購物車
}
# 取得 bob 的購物車,若不存在則給一個空列表
bob_cart = carts.get('bob', [])
bob_cart.append('notebook') # 只會在本次變數上加入
print(carts) # {'alice': ['book', 'pen'], 'bob': []}
print(bob_cart) # ['notebook']
注意:此範例使用
get()的返回值 不會寫回carts,若需要同步,需要自行賦值回字典,或改用setdefault()。
範例 4:與 setdefault() 結合,避免共享可變物件
from collections import defaultdict
# 使用 defaultdict 讓每個缺少的鍵自動得到一個新列表
scores = defaultdict(list)
# 為不同學生加入分數
scores['alice'].append(85)
scores['bob'].append(92)
print(scores) # defaultdict(<class 'list'>, {'alice': [85], 'bob': [92]})
說明:
defaultdict內部實作類似dict.get(key, default_factory()),每次缺少的鍵都會得到全新的列表,避免共享問題。
範例 5:在資料清理時使用 get() 取代多層 if
raw_data = [
{'id': 1, 'name': 'Tom', 'score': 78},
{'id': 2, 'name': 'Jerry'}, # 缺少 score
{'id': 3, 'name': 'Spike', 'score': 92},
]
# 我們想把每筆資料轉成 (id, name, score) 三元組,若缺少 score 則給 0
cleaned = [(d['id'], d['name'], d.get('score', 0)) for d in raw_data]
print(cleaned)
# [(1, 'Tom', 78), (2, 'Jerry', 0), (3, 'Spike', 92)]
優點:使用
get()讓列表生成式保持單行,程式碼更易讀且不易出錯。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 建議的做法 |
|---|---|---|
使用可變物件作為 default |
每次呼叫 get() 都會回傳同一個物件的參考,導致資料意外被共享。 |
若需要每次得到新物件,改用 setdefault()、defaultdict 或在 get() 後自行 copy()。 |
忽略 default 為 None 的情況 |
有時會誤以為 None 代表「有值」的情況,導致後續程式誤判。 |
明確檢查 is None,或在設計時使用特殊 sentinel 物件(如 object())作為預設值。 |
把 get() 與 in 檢查混用 |
兩者同時使用會降低效能,且失去 get() 的簡潔性。 |
只要需要值就使用 get(),只需要判斷鍵是否存在則使用 key in dict。 |
對不存在的鍵使用 dict[key] |
會拋出 KeyError,破壞程式流程。 |
使用 get() 或 setdefault(),除非你確定鍵一定存在。 |
在大型迴圈中頻繁呼叫 dict.get() |
若 default 為耗時運算,會影響效能。 |
可先將 default 計算結果儲存變數,或使用 defaultdict 讓字典自行產生。 |
最佳實踐小結
- 預設值盡量使用不可變物件(如
None、0、""),避免共享狀態。 - 需要自動建立新容器時,優先考慮
defaultdict或setdefault()。 - 若僅需判斷鍵是否存在,使用
if key in my_dict:,不要混用get()。 - 在函式或類別的參數預設值,避免直接寫
dict.get(key, []),改用default_factory或在函式內部處理。
實際應用場景
1. API 回傳資料的容錯處理
當呼叫外部服務時,回傳的 JSON 可能缺少某些欄位。使用 get() 可在不拋例外的情況下提供安全的預設值,讓後續資料處理流程順暢。
response = {
"user_id": 42,
"profile": {"nickname": "Bob"}
# 可能缺少 "email"
}
email = response.get("email", "unknown@example.com")
2. 統計計算中的計數器
在計算詞頻、點擊次數等統計時,常用字典累加值。get() 能簡化「不存在則從 0 開始」的寫法。
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
freq = {}
for w in words:
freq[w] = freq.get(w, 0) + 1
print(freq) # {'apple': 3, 'banana': 2, 'orange': 1}
3. 配置檔的預設值
開發大型系統時,常把設定寫在字典或 json 檔中。若使用者未提供某些設定項,get() 可快速補上預設值,避免程式在啟動時失敗。
config = {
"host": "localhost",
"port": 8080
# 沒有 "debug"
}
debug_mode = config.get("debug", False)
4. 資料清理與轉換
在 ETL(Extract‑Transform‑Load)流程中,資料欄位可能不完整。利用 get() 可一次完成缺失值填補與型別轉換。
raw = {"id": "001", "price": "12.5"} # price 可能是字串或缺失
price = float(raw.get("price", 0.0))
總結
dict.get() 是 Python 中 防止 KeyError、提供預設值 的利器。透過本篇文章,你應該已了解:
get()的基本語法與返回行為。- 為什麼在多數情況下它比直接索引更安全、更簡潔。
- 可變物件作為預設值時的共享問題與解決方案(
defaultdict、setdefault())。 - 多個實務範例,從基礎查詢、API 容錯、統計計數到設定管理,都能看到
get()的身影。 - 常見的陷阱與最佳實踐,讓你寫出更健全、易維護的程式碼。
掌握 get() 後,你的字典操作將變得 更具彈性、更具可讀性,也能在面對不確定或缺失資料時,保持程式的穩定性。未來在開發任何需要鍵值查詢的功能時,請先思考是否可以使用 dict.get() 取代繁瑣的 if 判斷或 try/except,讓程式碼更乾淨、效能更佳。祝你寫程式愉快,持續探索 Python 的無限可能!