できない.dev

Python の dict アクセスで KeyError が解消できない

KeyError は dict に存在しないキーを d[key] で参照したときに発生する。
事前チェックなら `if key in d`、デフォルト値を返すなら d.get(key, default)、欠落キーを許容したいなら collections.defaultdict を使い分ける。
スペース・改行・大文字小文字違いが原因のことも多い。

#dict#keyerror#exception#defaultdict

公開:

要約

KeyError は dict に存在しないキーを d[key] で参照したときに必ず発生する例外。
回避策は 3 つ: 事前に key in d で確認する、d.get(key, default) でデフォルト値を返す、collections.defaultdict で「キーが無ければ自動で空値を作る」運用にする。
例外メッセージにキー名がそのまま出るので、見えない空白や改行が混じっていないかも要確認。

よくある原因

  1. API レスポンスや JSON の構造が想定と違う。
    OpenAPI で required でないフィールドを d["foo"] で取りに行って落ちる。
  2. CSV ヘッダや環境変数の読み込み時に、末尾の改行や半角スペースが残ったままキーになっている。
  3. HTTP ヘッダや YAML の dict は大文字小文字の扱いが処理系で違う。d["Content-Type"]d["content-type"] は別キー。
  4. for ループの中で d[f"item_{i}"] のようにキーを組み立てるとき、i の範囲が dict にない値まで含む。

解決策

1. d.get() を使う

value = d.get("name")            # 無ければ None
value = d.get("name", "anon")    # 無ければ "anon"

公式ドキュメント の挙動どおり、例外を出さずに済む。
戻り値が None になり得る点に注意し、後段の属性アクセス前に is not None で絞ると安全。

2. in で先にチェックする

if "name" in d:
    print(d["name"])

「キーがあるときだけ何かする」明示的なフローを書きたい場合はこちら。

3. defaultdict で初期値を任せる

from collections import defaultdict
 
counts = defaultdict(int)
for word in words:
    counts[word] += 1            # 初出キーは 0 から開始

リスト追加なら defaultdict(list)、ネスト dict なら defaultdict(dict) を使う。
挙動の詳細は collections 公式ドキュメント を参照。

4. キー文字列を正規化する

clean = {k.strip().lower(): v for k, v in raw.items()}
clean["content-type"]

CSV / 環境変数 / HTTP ヘッダから dict を作るときは、読み込み直後に空白除去と小文字化をしておくと「見えないバグ」を予防できる。

この記事は役立ちましたか?