閉じる

【Python】辞書でキー存在をチェックする3つの書き方(in,get)

辞書でキーの存在確認を間違えると、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-exceptKeyErrorを扱う(例: 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や空文字などの偽値でも正しく扱えます。

Python
# 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や空文字が「ない」と解釈されます。

Python
# よくある誤り: 値の真偽で存在判定してしまう
d = {"count": 0, "name": ""}

if d.get("count"):
    print("count exists")     # 実行されない(0は偽のため)
else:
    print("count is missing") # 誤判定
実行結果
count is missing

正しくはinで存在判定し、必要なら値を取り出します。

Python
# 正しい: 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 dkey in d.keys()と同じ意味ですが、keys()を明示する必要はありません。

簡潔に書けて可読性も上がります。

Python
d = {"a": 1}

print("a" in d)         # これでOK
print("a" in d.keys())  # 不要(意味は同じ)
実行結果
True
True

補足: keys()はビューオブジェクトを返し、集合的な操作が必要なとき(例: d.keys() & other_keys)に活用します。

getでチェックする

dict.getでデフォルト値を返す

getキーが存在しないときにデフォルト値を返す便利なメソッドです。

存在しなければ特定の値を使いたい場面に向きます。

Python
# get で値を取得しつつ、なければデフォルト値を返す
config = {"timeout": 0}

print(config.get("timeout", 10))  # 0(存在するので辞書の値が返る)
print(config.get("retries", 3))   # 3(存在しないのでデフォルト)
実行結果
0
3

ポイント: デフォルト引数は呼び出し時に評価されます。

重いオブジェクト生成をデフォルトに書くと無駄に作られる可能性があるため、必要なら関数や遅延生成の仕組みを使うか、先にinで分岐します。

Noneや0を区別するためのセンチネル

値としてNone0が入る可能性がある場合、センチネル(決して辞書に入らないユニークなオブジェクト)で「存在しない」と区別します。

Python
# センチネルで 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行で書けます。

Python
# 代入式 + センチネルで同時に存在判定と取得
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だけを捕まえて対処します。

Python
# EAFP: まずアクセスして、なければ KeyError を処理
data = {"size": 42}

try:
    size = data["size"]  # 期待通り存在することを前提に書く
    print("size =", size)
except KeyError:
    print("size is missing")
実行結果
size = 42

存在しない場合は例外ブロックに入ります。

Python
data = {}

try:
    size = data["size"]
    print("size =", size)
except KeyError:
    print("size is missing")
実行結果
size is missing

チェック+処理を一度に書くパターン

EAFPは「存在することが普通、欠損は例外的」な場面で読みやすく、処理を一度に書けます。

たとえばカウンタの加算です。

Python
# 辞書内カウンタをインクリメントする関数
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: 「ほぼ存在する」前提で、処理を一度に書きたいときに向きます。欠損が多いと例外コストで遅くなるため、その場合はingetが無難です。

以下に特徴を整理します。

方法目的値の取得同時偽値の安全性向く場面注意点
in存在判定いいえ(別行)高い有無だけ知りたい値が必要なら2度アクセスになる
get取得と既定値はいセンチネル併用で高いなければ既定値で処理既定値は即時評価される
try-except処理を一気にはい高い存在が普通で欠損が例外欠損が多いと遅い、exceptを絞る

結論として、まずはin、次にget、状況によってtry-exceptという順で検討すると安全で読みやすいコードになりやすいです。

まとめ

辞書のキー存在確認は、誤判定を避ける設計が何より重要です。

単純な存在確認はin、デフォルト値を使いたいときはget、存在が前提で処理を簡潔に書きたいときはtry-exceptを選ぶとよいです。

特に0や空文字、Noneを「ない」と誤解するミスは起こりやすいので、inでの判定センチネルの活用を習慣にしてください。

これらの基本を押さえることで、現場の辞書操作はぐっと安全で読みやすくなります。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!