Python KeyErrorの原因と対処法まとめ
PythonのKeyErrorは、辞書(dict)に存在しないキーでアクセスしようとしたときに発生するエラーです。データ処理やAPI連携など辞書を扱う場面は多く、避けて通れないエラーの一つです。この記事では、KeyErrorの発生パターンと具体的な対処法をコード例とともに解説します。
KeyErrorとは
KeyErrorは、辞書から存在しないキーの値を取得しようとしたときにPythonが発生させる例外です。辞書はキーと値のペアでデータを管理する構造ですが、指定したキーが辞書に含まれていないと処理を続行できません。
エラーメッセージの読み方
user = {"name": "太郎", "age": 25}
print(user["email"])
# KeyError: 'email'
エラーメッセージにはアクセスしようとしたキー名が表示されます。この例では辞書userに"email"というキーが存在しないためエラーになっています。
よくある発生場面
KeyErrorは以下のような場面で多く発生します。
- APIレスポンスのJSONデータから値を取り出すとき
- CSVファイルを辞書形式で読み込んで処理するとき
- 設定ファイルの値を参照するとき
- ユーザー入力に基づいて辞書を検索するとき
パターン1: 存在しないキーへの直接アクセス
最も基本的なKeyErrorのパターンです。
# NG: 存在しないキーに直接アクセス
config = {"host": "localhost", "port": 8080}
print(config["database"])
# KeyError: 'database'
getメソッドで安全にアクセスする
辞書のgetメソッドを使うと、キーが存在しない場合にデフォルト値を返すことができます。
# OK: getメソッドを使う
config = {"host": "localhost", "port": 8080}
database = config.get("database")
print(database) # None
# デフォルト値を指定する
database = config.get("database", "mydb")
print(database) # "mydb"
in演算子で事前にチェックする
キーの存在をin演算子で確認してからアクセスする方法もあります。
# OK: in演算子で事前チェック
config = {"host": "localhost", "port": 8080}
if "database" in config:
print(config["database"])
else:
print("databaseキーが見つかりません")
パターン2: キー名のスペルミスや大文字小文字の違い
キー名が微妙に異なっていてKeyErrorになるケースです。
# NG: キー名のスペルミス
response = {"status_code": 200, "message": "OK"}
print(response["statusCode"])
# KeyError: 'statusCode'
Pythonの辞書ではキー名の大文字小文字やアンダースコアの有無が厳密に区別されます。
キー一覧を確認して修正する
# デバッグ: 辞書のキー一覧を確認
response = {"status_code": 200, "message": "OK"}
print(response.keys())
# dict_keys(['status_code', 'message'])
# 正しいキー名でアクセス
print(response["status_code"]) # 200
API連携での注意点
外部APIから受け取るJSONデータはキー名の規則がサービスごとに異なります。キャメルケース(statusCode)とスネークケース(status_code)が混在していることもあるため、レスポンスの構造を事前に確認しましょう。
import json
# APIレスポンスの構造を確認
response_text = '{"statusCode": 200, "userName": "Taro"}'
data = json.loads(response_text)
print(json.dumps(data, indent=2))
# キー名を確認してからアクセス
print(data["statusCode"]) # 200
パターン3: ネストした辞書でのKeyError
辞書が入れ子になっている場合、途中のキーが存在しないとKeyErrorになります。
# NG: ネストした辞書のアクセスでエラー
user = {
"name": "太郎",
"address": {
"city": "東京"
}
}
print(user["address"]["zipcode"])
# KeyError: 'zipcode'
段階的にチェックする
# OK: 段階的にキーの存在を確認
user = {
"name": "太郎",
"address": {
"city": "東京"
}
}
if "address" in user and "zipcode" in user["address"]:
print(user["address"]["zipcode"])
else:
print("郵便番号が登録されていません")
try-exceptで処理する
深くネストした辞書の場合は、try-exceptを使うほうがコードが簡潔になることがあります。
# OK: try-exceptでKeyErrorを捕捉
user = {
"name": "太郎",
"address": {
"city": "東京"
}
}
try:
zipcode = user["address"]["zipcode"]
print(f"郵便番号: {zipcode}")
except KeyError as e:
print(f"キー {e} が見つかりません")
# キー 'zipcode' が見つかりません
パターン4: forループ中のKeyError
辞書のリストを処理するとき、一部のデータにキーが欠けているとKeyErrorが発生します。
# NG: データの一部にキーが欠けている
users = [
{"name": "太郎", "email": "[email protected]"},
{"name": "花子"}, # emailキーがない
{"name": "次郎", "email": "[email protected]"}
]
for user in users:
print(user["email"])
# 1回目は成功、2回目でKeyError: 'email'
getメソッドでループを安全にする
# OK: getメソッドでデフォルト値を設定
users = [
{"name": "太郎", "email": "[email protected]"},
{"name": "花子"},
{"name": "次郎", "email": "[email protected]"}
]
for user in users:
email = user.get("email", "未登録")
print(f"{user['name']}: {email}")
# 太郎: [email protected]
# 花子: 未登録
# 次郎: [email protected]
データの前処理で欠損を補完する
大量のデータを扱う場合は、ループの前にデータを整形しておく方法が効果的です。
# OK: 事前にデフォルト値を設定
default_user = {"name": "不明", "email": "未登録", "age": 0}
users = [
{"name": "太郎", "email": "[email protected]"},
{"name": "花子"},
]
for user in users:
complete_user = {**default_user, **user}
print(f"{complete_user['name']}: {complete_user['email']}")
# 太郎: [email protected]
# 花子: 未登録
パターン5: defaultdictで自動的にデフォルト値を設定する
標準ライブラリのcollections.defaultdictを使うと、存在しないキーへのアクセス時にデフォルト値が自動的に生成されます。
カウント処理での活用
from collections import defaultdict
# 通常の辞書だとKeyErrorになる
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
# NG: 通常の辞書
counts = {}
for word in words:
counts[word] += 1 # KeyError: 'apple'(初回アクセス時)
# OK: defaultdictを使う
counts = defaultdict(int)
for word in words:
counts[word] += 1 # 存在しないキーは自動的に0で初期化
print(dict(counts))
# {'apple': 3, 'banana': 2, 'cherry': 1}
グルーピング処理での活用
from collections import defaultdict
# データをカテゴリ別にグルーピング
items = [
("果物", "りんご"),
("野菜", "にんじん"),
("果物", "みかん"),
("野菜", "キャベツ"),
]
groups = defaultdict(list)
for category, item in items:
groups[category].append(item)
print(dict(groups))
# {'果物': ['りんご', 'みかん'], '野菜': ['にんじん', 'キャベツ']}
パターン6: setdefaultメソッドの活用
setdefaultメソッドは、キーが存在しない場合にデフォルト値を設定しつつ値を返します。
# setdefaultの使い方
config = {"host": "localhost"}
# キーがなければ設定して返す
port = config.setdefault("port", 8080)
print(port) # 8080
print(config) # {'host': 'localhost', 'port': 8080}
# キーが既にあれば既存の値を返す
host = config.setdefault("host", "0.0.0.0")
print(host) # 'localhost'(既存の値が維持される)
getとsetdefaultの使い分け
config = {"host": "localhost"}
# get: 辞書を変更しない(読み取り専用の場面に適する)
value = config.get("port", 8080)
print(config) # {'host': 'localhost'} ← 辞書は変わらない
# setdefault: 辞書にデフォルト値を追加する(初期化の場面に適する)
value = config.setdefault("port", 8080)
print(config) # {'host': 'localhost', 'port': 8080} ← 辞書が更新される
まとめ
KeyErrorは辞書に存在しないキーへアクセスしたときに発生します。対処法を整理すると以下の通りです。
getメソッドでデフォルト値を指定して安全にアクセスするin演算子でキーの存在を事前にチェックするtry-exceptでKeyErrorを捕捉して代替処理を行うdefaultdictやsetdefaultで自動的にデフォルト値を設定する- キー名のスペルミスや大文字小文字の違いに注意する
辞書を使う場面では「このキーは必ず存在するか」を常に意識することが、KeyErrorを防ぐ最も確実な方法です。