辞書でキーの存在確認を間違えると、0や空文字(“”)、Noneなどの値を「ない」と誤判定して思わぬバグを招きます。
本記事では、Pythonで辞書のキーが存在するかを安全かつ読みやすく確認する3つの代表的な書き方(in、get、try-except)を、初心者の方にもわかりやすく順を追って解説します。
用途に合わせた使い分けや落とし穴も丁寧に説明します。
辞書のキー存在を確認する基本
3つの書き方(in,get,try-except)
辞書のキーが存在するかを確認する方法は主に次の3つです。
どれもPythonでは一般的で、状況に応じて使い分けます。
- inで確認する(例:
if key in d:
)。最も素直な書き方で、存在確認の第一選択です。 - getで取得しつつ確認する(例:
v = d.get(key, default)
)。存在確認と値の取得を同時に行えるのが利点です。 - try-exceptで
KeyError
を扱う(例:d[key]
)。PythonでよくあるEAFP(例外で流れを制御)のスタイルです。
Pythonの辞書で安全にチェックするポイント
辞書における安全なチェックのコツは次の通りです。
特に偽と評価される値(0、False、空文字、None)の扱いに注意します。
- inはキーの存在のみを判定します。値が0や空文字でも誤判定しません。
get
はデフォルト値を返すので、値が0やNoneのときに「ない」と誤解しないようにします。必要ならセンチネル(ユニークなオブジェクト)で区別します。- keys()での判定(
key in d.keys()
)は不要です。key in d
で十分です。 try-except
は存在するときに速く、存在しないケースが多いと遅い傾向があります。処理の流れと期待値で選びます。
inでチェックする
if key in dictの基本と安全性
キーの存在確認だけならinが最も直感的で安全です。
0や空文字などの偽値でも正しく扱えます。
# in を使った基本的な存在確認
d = {"count": 0, "name": "", "enabled": False, "note": None}
for k in ["count", "missing"]:
if k in d: # キーの存在だけを見る
print(f"{k} exists, value={d[k]!r}")
else:
print(f"{k} not found")
count exists, value=0
missing not found
False系の値(0,None,””)でも誤判定しない
次の例は間違った書き方です。
get
で取得した値をif
で直接評価すると、0や空文字が「ない」と解釈されます。
# よくある誤り: 値の真偽で存在判定してしまう
d = {"count": 0, "name": ""}
if d.get("count"):
print("count exists") # 実行されない(0は偽のため)
else:
print("count is missing") # 誤判定
count is missing
正しくはinで存在判定し、必要なら値を取り出します。
# 正しい: in で存在判定してから値にアクセス
d = {"count": 0, "name": ""}
if "count" in d:
print("count exists, value =", d["count"])
else:
print("count is missing")
count exists, value = 0
keys()での判定は不要
key in d
はkey in d.keys()
と同じ意味ですが、keys()
を明示する必要はありません。
簡潔に書けて可読性も上がります。
d = {"a": 1}
print("a" in d) # これでOK
print("a" in d.keys()) # 不要(意味は同じ)
True
True
補足: keys()
はビューオブジェクトを返し、集合的な操作が必要なとき(例: d.keys() & other_keys
)に活用します。
- 組み込み型 — dict(マッピング型) — Python 3.13.7 ドキュメント
- 式 — メンバーシップテスト in/not in — Python 3.13.7 ドキュメント
- 組み込み型 — dict.get — Python 3.13.7 ドキュメント
getでチェックする
dict.getでデフォルト値を返す
get
はキーが存在しないときにデフォルト値を返す便利なメソッドです。
存在しなければ特定の値を使いたい場面に向きます。
# get で値を取得しつつ、なければデフォルト値を返す
config = {"timeout": 0}
print(config.get("timeout", 10)) # 0(存在するので辞書の値が返る)
print(config.get("retries", 3)) # 3(存在しないのでデフォルト)
0
3
ポイント: デフォルト引数は呼び出し時に評価されます。
重いオブジェクト生成をデフォルトに書くと無駄に作られる可能性があるため、必要なら関数や遅延生成の仕組みを使うか、先にin
で分岐します。
Noneや0を区別するためのセンチネル
値としてNone
や0
が入る可能性がある場合、センチネル(決して辞書に入らないユニークなオブジェクト)で「存在しない」と区別します。
# センチネルで None と「存在しない」を区別する
settings = {"path": None, "retries": 0}
_sentinel = object() # ユニークな印
v = settings.get("path", _sentinel)
print(v is _sentinel) # False(キーは存在し、値は None)
w = settings.get("missing", _sentinel)
print(w is _sentinel) # True(キーは存在しない)
False
True
存在判定と値取得を同時に行う
Python 3.8以降なら、:=
(代入式、いわゆるワルラス演算子)とセンチネルを組み合わせ、存在判定と値の取得を1行で書けます。
# 代入式 + センチネルで同時に存在判定と取得
env = {"HOME": "/home/user", "SHELL": ""}
S = object()
if (home := env.get("HOME", S)) is not S:
print("HOME =", home)
if (shell := env.get("SHELL", S)) is not S:
print("SHELL =", shell)
if (tmp := env.get("TMPDIR", S)) is not S:
print("TMPDIR =", tmp)
else:
print("TMPDIR is missing")
HOME = /home/user
SHELL =
TMPDIR is missing
注意: 値としてNone
や空文字が来ても、そのまま正しく扱えます。
try-except(KeyError)でEAFP
例外で存在しないキーを扱う
EAFP(Easier to Ask for Forgiveness than Permission: 許可より謝罪が易しい)の流儀では、d[key]
で直接アクセスし、KeyError
だけを捕まえて対処します。
# EAFP: まずアクセスして、なければ KeyError を処理
data = {"size": 42}
try:
size = data["size"] # 期待通り存在することを前提に書く
print("size =", size)
except KeyError:
print("size is missing")
size = 42
存在しない場合は例外ブロックに入ります。
data = {}
try:
size = data["size"]
print("size =", size)
except KeyError:
print("size is missing")
size is missing
チェック+処理を一度に書くパターン
EAFPは「存在することが普通、欠損は例外的」な場面で読みやすく、処理を一度に書けます。
たとえばカウンタの加算です。
# 辞書内カウンタをインクリメントする関数
def inc(counter: dict, key: str, delta: int = 1) -> None:
try:
counter[key] += delta # ある前提で処理を書く
except KeyError:
counter[key] = delta # なければ初期化
c = {}
inc(c, "apple")
inc(c, "apple")
inc(c, "banana", 3)
print(c)
{'apple': 2, 'banana': 3}
同様に、存在すれば加工、なければ作成といった「チェック+処理」をひとかたまりで書けます。
in/getとの使い分け(可読性と性能)
どれを選ぶかは読みやすさと期待ケース(ヒット/ミスの比率)で決めます。
- in: 「存在の有無だけ」が目的なら最優先。偽値も誤判定しません。後続で必ず値を使うなら
if key in d: v = d[key]
の2段でも読みやすいです。 - get: 「なければデフォルトで処理」が自然なときに最適。センチネルを併用すれば存在判定と取得を同時に安全に行えます。
- try-except: 「ほぼ存在する」前提で、処理を一度に書きたいときに向きます。欠損が多いと例外コストで遅くなるため、その場合は
in
やget
が無難です。
以下に特徴を整理します。
方法 | 目的 | 値の取得同時 | 偽値の安全性 | 向く場面 | 注意点 |
---|---|---|---|---|---|
in | 存在判定 | いいえ(別行) | 高い | 有無だけ知りたい | 値が必要なら2度アクセスになる |
get | 取得と既定値 | はい | センチネル併用で高い | なければ既定値で処理 | 既定値は即時評価される |
try-except | 処理を一気に | はい | 高い | 存在が普通で欠損が例外 | 欠損が多いと遅い、exceptを絞る |
結論として、まずはin、次にget、状況によってtry-exceptという順で検討すると安全で読みやすいコードになりやすいです。
まとめ
辞書のキー存在確認は、誤判定を避ける設計が何より重要です。
単純な存在確認はin、デフォルト値を使いたいときはget、存在が前提で処理を簡潔に書きたいときはtry-exceptを選ぶとよいです。
特に0や空文字、Noneを「ない」と誤解するミスは起こりやすいので、inでの判定やセンチネルの活用を習慣にしてください。
これらの基本を押さえることで、現場の辞書操作はぐっと安全で読みやすくなります。