HOWTO · Python

Python 辞書の値からキーを検索

このチュートリアルでは、Python の辞書で値によってキーを取得する方法を示します。

このページの内容

Python の辞書はキーと値を対応付けているため、値からキーを探すには辞書のエントリーを検索します。キーと値のペアを順に調べ、値を検索対象と比較します。最初に一致したキーが必要ならループまたは next() を使い、一致するキーをすべて取得するならリスト内包表記を使います。いずれの方法も、一致が見つかるか辞書を最後まで調べるまでエントリーを走査します。

ループでキーと値のペアを検索する

dict.items() は、キーと値のペアを反復できるオブジェクトを返します。これをループで調べれば、値が検索対象と一致する最初のキーを返せます。ループの後に None を返すと、一致がない場合の結果を呼び出し側に明確に示せます。None 自体が有効なキーになり得る場合は、別のセンチネル値を使うか、代わりに (found, key) の組を返してください。

次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

def find_key_by_value(dictionary, target):
    for key, value in dictionary.items():
        if value == target:
            return key
    return None

scores = {"Ada": 91, "Lin": 87, "Sam": 91}
print(find_key_by_value(scores, 87))
Lin

一致が見つかるとループはすぐに戻るため、返されるのは辞書の反復順序で最初に見つかったキーです。同じ値を持つキーが複数あっても、最初の一致以降は調べません。

next() で最初に一致するキーを取得する

ジェネレーター式を使うと、同じ最初の一致検索を簡潔に表せます。デフォルト値を指定して next() に渡せば、一致するキーがジェネレーターにない場合も StopIteration を送出せず、呼び出しは None を返します。

検索対象を引数として受け取る関数内で式を評価すれば、一致する値と一致しない値のどちらでも動作します。

このセクションのコードと表示されるラベルや出力は英語のままです。

def find_key_with_next(dictionary, target):
    return next((key for key, value in dictionary.items() if value == target), None)

scores = {"Ada": 91, "Lin": 87, "Sam": 91}
print(find_key_with_next(scores, 87))
print(find_key_with_next(scores, 100))

ジェネレーターは必要に応じてキーを生成するため、一致するキーをすべて格納したリストは作りません。値が一致しない場合はデフォルト値が返されます。データに None がキーとして含まれる可能性がある場合は、有効なキーと区別できるデフォルト値を選んでください。

内包表記ですべての一致キーを集める

値の重複をすべて扱う必要がある場合は、リスト内包表記で一致するキーをすべて集めます。

このセクションのコードと表示されるラベルや出力は英語のままです。

def find_all_keys(dictionary, target):
    return [key for key, value in dictionary.items() if value == target]

scores = {"Ada": 91, "Lin": 87, "Sam": 91}
print(find_all_keys(scores, 91))
['Ada', 'Sam']

値が一致しなければ返されるリストは空になり、一致すれば該当するキーがすべて含まれます。filter() 式でも一致するキーと値のペアを選べますが、内包表記なら条件と集めるキーを一か所で確認できます。

対応するキーと値のリストからキーを探す

別の方法として、dict.keys() と dict.values() をリストに変換できます。両者の位置は対応しており、位置 i のキーは位置 i の値に対応します。値のリストに対して index() を呼び出すと、検索対象が最初に現れる位置が得られます。その位置を使って、キーのリストから対応するキーを選びます。

このセクションのコードと表示されるラベルや出力は英語のままです。

scores = {"Ada": 91, "Lin": 87, "Sam": 91}
keys = list(scores.keys())
values = list(scores.values())
position = values.index(91)
print(keys[position])
Ada

値が存在しない場合、index() は ValueError を送出します。不在が想定されるなら、先に values に値が含まれるか確認するか、そのエラーを処理してください。値が重複している場合、index() が選ぶのは最初に一致する位置だけです。

重複値、見つからない値、処理コストへの対処

必要な結果に応じて方法を選びます。ループまたは next() はキーを1つ返し、内包表記は一致するキーすべてのリストを返します。最初の一致を探す方法と index() は、最初に一致した時点で止まり、その結果は辞書の反復順序に左右されます。一致しない場合は None または空のリストを返せますが、対応するリストを使う方法では、値の存在を確認するかエラーを処理しない限り ValueError が送出されます。

どの方法でも、最悪の場合はすべてのエントリーを調べるため、検索時間は辞書のエントリー数に比例します。ループとジェネレーター式は一致結果のコレクションを作りません。内包表記は結果のリストを作り、対応リストの方法では検索前にキーと値のリストを別々に作ります。値からキーへの検索を繰り返すなら、逆引き用の対応表を維持する方法もあります。単純な逆引き辞書では値ごとに1つのキーしか保持できないため、値の重複を考慮してください。ネストした辞書を検索する場合は、まずどの階層を調べるかを決め、その辞書に同じエントリー反復の方法を適用します。