本文 AI 產出,尚未審核

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 讓字典自行產生。

最佳實踐小結

  1. 預設值盡量使用不可變物件(如 None、0、""),避免共享狀態。
  2. 需要自動建立新容器時,優先考慮 defaultdict 或 setdefault()。
  3. 若僅需判斷鍵是否存在,使用 if key in my_dict:,不要混用 get()。
  4. 在函式或類別的參數預設值,避免直接寫 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 的無限可能!