Python KeyError例外とそれらの処理方法
PythonのKeyError
例外は、初心者が遭遇する一般的な例外です。 KeyError
を上げることができる理由と、それがプログラムを停止しないようにするためのいくつかの解決策を知ることは、Pythonプログラマーとして向上するための重要なステップです。
このチュートリアルを終了すると、次のことがわかります。
-
Python
KeyError
の通常の意味 -
他の場所では、標準ライブラリの
KeyError
が表示される可能性があります -
あなたがそれを見たときに
KeyError
を処理する方法
Free Bonus:Click here to get our free Python Cheat Sheetは、データ型、辞書、リスト、Python関数の操作など、Python3の基本を示しています。
PythonKeyError
の通常の意味
PythonKeyError
exceptionは、dictionary(dict
)にないキーにアクセスしようとしたときに発生するものです。
Pythonのofficial documentationは、マッピングキーにアクセスしたときにKeyError
が発生し、マッピングに見つからないことを示しています。 マッピングは、1つの値セットを別の値セットにマップするデータ構造です。 Pythonで最も一般的なマッピングは辞書です。
PythonKeyError
はLookupError
例外の一種であり、探していたキーの取得に問題があったことを示しています。 KeyError
が表示されている場合、意味的な意味は、探しているキーが見つからなかったことです。
以下の例では、3人の年齢で定義された辞書(ages
)を見ることができます。 辞書にないキーにアクセスしようとすると、KeyError
が発生します。
>>>
>>> ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
>>> ages['Michael']
Traceback (most recent call last):
File "", line 1, in
KeyError: 'Michael'
ここで、ages
ディクショナリのキー'Michael'
にアクセスしようとすると、KeyError
が発生します。 トレースバックの下部で、関連情報を取得します。
-
KeyError
が発生したという事実 -
見つからなかったキー、つまり
'Michael'
最後から2番目の行は、どの行で例外が発生したかを示しています。 この情報は、ファイルからPythonコードを実行する場合に役立ちます。
Note: Pythonで例外が発生すると、tracebackで例外が発生します。 トレースバックを使用すると、すべての関連情報が得られ、例外が発生した理由とその原因を特定できます。
Pythonトレースバックの読み方を学び、それがあなたに何を伝えているかを理解することは、Pythonプログラマーとして改善するために重要です。
以下のプログラムでは、ages
ディクショナリが再度定義されているのを確認できます。 今回は、次の年齢を取得する人の名前を入力するよう求められます。
1 # ages.py
2
3 ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
4 person = input('Get age for: ')
5 print(f'{person} is {ages[person]} years old.')
このコードは、プロンプトで指定した名前を使用して、その人の年齢を取得しようとします。 プロンプトで入力したものはすべて、4行目のages
辞書のキーとして使用されます。
上記の失敗した例を繰り返すと、別のトレースバックが得られます。今回は、KeyError
が発生したファイル内の行に関する情報が含まれています。
$ python ages.py
Get age for: Michael
Traceback (most recent call last):
File "ages.py", line 4, in
print(f'{person} is {ages[person]} years old.')
KeyError: 'Michael'
辞書にないキーを指定すると、プログラムは失敗します。 ここで、トレースバックの最後の数行は問題を示しています。 File "ages.py", line 4, in <module>
は、どのファイルのどの行でKeyError
例外が発生したかを示します。 次に、その行が表示されます。 最後に、KeyError
例外は、欠落しているキーを提供します。
したがって、KeyError
トレースバックの最後の行では、それだけでは十分な情報が得られないことがわかりますが、その前の行では、何が悪かったのかをより深く理解することができます。
Note:上記の例と同様に、このチュートリアルの他の例のほとんどは、Python 3.6で導入されたf-stringsを利用しています。
他の場所で標準ライブラリにPythonKeyError
が表示される可能性があります
ほとんどの場合、キーがディクショナリまたはディクショナリサブクラス(os.environ
など)に見つからないため、PythonKeyError
が発生します。
まれに、アイテムがZIPアーカイブに見つからない場合、zipfile
モジュールなど、Pythonの標準ライブラリの他の場所で発生することがあります。 ただし、これらの場所は、要求されたキーを検出しないPythonKeyError
と同じ意味を保持します。
次の例では、zipfile.ZipFile
クラスを使用して、.getinfo()
を使用してZIPアーカイブに関する情報を抽出していることがわかります。
>>>
>>> from zipfile import ZipFile
>>> zip_file = ZipFile('the_zip_file.zip')
>>> zip_file.getinfo('something')
Traceback (most recent call last):
File "", line 1, in
File "/path/to/python/installation/zipfile.py", line 1304, in getinfo
'There is no item named %r in the archive' % name)
KeyError: "There is no item named 'something' in the archive"
これは実際には辞書キー検索のようには見えません。 代わりに、例外を発生させるのはzipfile.ZipFile.getinfo()
の呼び出しです。
トレースバックも少し異なって見えますが、欠落しているキーKeyError: "There is no item named 'something' in the archive"
よりも少し多くの情報が提供されています。
ここで最後に注意することは、KeyError
を上げた行がコードにないことです。 これはzipfile
コードにありますが、トレースバックの前の行は、コードのどの行が問題を引き起こしたかを示しています。
独自のコードでPythonKeyError
を発生させる必要がある場合
you to raiseの場合、独自のコードでPythonKeyError
例外が意味をなす場合があります。 これは、raise
キーワードを使用し、KeyError
例外を呼び出すことで実行できます。
raise KeyError(message)
通常、message
は欠落しているキーになります。 ただし、zipfile
パッケージの場合と同様に、次の開発者が何が悪かったのかをよりよく理解できるように、もう少し情報を提供することを選択できます。
独自のコードでPythonKeyError
を発生させることにした場合は、ユースケースが例外の背後にあるセマンティックな意味と一致することを確認してください。 探しているキーが見つからなかったことを示す必要があります。
あなたがそれを見たときにPythonKeyError
を処理する方法
KeyError
に遭遇した場合、それを処理するためのいくつかの標準的な方法があります。 ユースケースによっては、これらのソリューションのいくつかは他のソリューションよりも優れている場合があります。 最終的な目標は、予期しないKeyError
例外が発生しないようにすることです。
通常の解決策:.get()
KeyError
が、独自のコードで失敗したディクショナリキールックアップから生成された場合、.get()
を使用して、指定されたキーで見つかった値またはデフォルト値を返すことができます。
前の年齢検索の例と同様に、次の例は、プロンプトで提供されるキーを使用して辞書から年齢を取得するより良い方法を示しています。
1 # ages.py
2
3 ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
4 person = input('Get age for: ')
5 age = ages.get(person)
6
7 if age:
8 print(f'{person} is {age} years old.')
9 else:
10 print(f"{person}'s age is unknown.")
ここで、5行目は、.get()
を使用してages
から年齢値を取得する方法を示しています。 これにより、age
変数は、提供されたキーのディクショナリで見つかった経過時間値、またはデフォルト値(この場合はNone
)になります。
今回は、キーに直接アクセスしようとするのではなく、より安全な.get()
メソッドを使用して経過時間を取得するため、KeyError
例外が発生することはありません。
$ python ages.py
Get age for: Michael
Michael's age is unknown.
上記の実行例では、不正なキーが提供されたときにKeyError
が発生しなくなりました。 キー'Michael'
は辞書にありませんが、.get()
を使用すると、発生したKeyError
ではなくNone
が返されます。
age
変数には、辞書にある人の年齢またはデフォルト値(デフォルトではNone
)のいずれかが含まれます。 2番目の引数を渡すことにより、.get()
呼び出しで別のデフォルト値を指定することもできます。
これは上記の例の5行目で、.get()
を使用して異なるデフォルトの経過時間を指定しています。
age = ages.get(person, 0)
ここでは、'Michael'
がNone
を返す代わりに、キーが見つからないため0
を返し、返されるデフォルト値は0
になりました。
まれな解決策:キーの確認
辞書内のキーの存在を判断する必要がある場合があります。 このような場合、.get()
を使用しても、正しい情報が得られない可能性があります。 .get()
の呼び出しからNone
が返されるということは、キーが見つからなかったか、ディクショナリのキーで見つかった値が実際にはNone
であることを意味している可能性があります。
辞書または辞書のようなオブジェクトでは、in
演算子を使用して、キーがマッピングに含まれているかどうかを判別できます。 この演算子は、キーが辞書にあるかどうかを示すブール値(True
またはFalse
)を返します。
この例では、APIの呼び出しからresponse
ディクショナリを取得しています。 この応答には、応答で定義されたerror
キー値が含まれている可能性があります。これは、応答がエラー状態にあることを示します。
1 # parse_api_response.py
2 ...
3 # Assuming you got a `response` from calling an API that might
4 # have an error key in the `response` if something went wrong
5
6 if 'error' in response:
7 ... # Parse the error state
8 else:
9 ... # Parse the success state
ここでは、error
キーがresponse
に存在するかどうかを確認することと、キーからデフォルト値を取得することには違いがあります。 これは、実際に探しているのはキーが辞書にあるかどうかであり、そのキーの値ではないというまれなケースです。
一般的な解決策:try
except
他の例外と同様に、いつでもtry
except
ブロックを使用して、潜在的な例外発生コードを分離し、バックアップソリューションを提供できます。
以前と同様の例でtry
except
ブロックを使用できますが、今回は、通常の場合にKeyError
が発生した場合に出力されるデフォルトのメッセージを提供します。
1 # ages.py
2
3 ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
4 person = input('Get age for: ')
5
6 try:
7 print(f'{person} is {ages[person]} years old.')
8 except KeyError:
9 print(f"{person}'s age is unknown.")
ここでは、人の名前と年齢を印刷するtry
ブロックで通常のケースを確認できます。 バックアップケースはexcept
ブロックにあり、通常のケースでKeyError
が発生した場合、バックアップケースは別のメッセージを出力します。
try
except
ブロックソリューションは、.get()
またはin
演算子をサポートしていない可能性のある他の場所にも最適なソリューションです。 KeyError
が他の人のコードから取得されている場合にも、これは最良の解決策です。
これは、zipfile
パッケージを再度使用した例です。 今回は、try
except
ブロックにより、KeyError
例外によるプログラムの停止を停止する方法が提供されます。
>>>
>>> from zipfile import ZipFile
>>> zip = ZipFile('the_zip_file.zip')
>>> try:
... zip.getinfo('something')
... except KeyError:
... print('Can not find "something"')
...
Can not find "something"
辞書のようにZipFile
クラスは.get()
を提供しないため、try
except
ソリューションを使用する必要があります。 この例では、.getinfo()
に渡すのに有効な値を事前に知る必要はありません。
結論
これで、PythonのKeyError
例外が発生する可能性のあるいくつかの一般的な場所と、それらがプログラムを停止しないようにするために使用できるいくつかの優れたソリューションがわかりました。
さて、次にKeyError
が発生したのを見ると、それはおそらく辞書キーのルックアップが悪いだけであることがわかります。 また、トレースバックの最後の数行を調べることで、エラーの原因を特定するために必要なすべての情報を見つけることができます。
問題が独自のコードでの辞書キー検索である場合は、辞書でキーに直接アクセスすることから、デフォルトの戻り値でより安全な.get()
メソッドを使用するように切り替えることができます。 問題が独自のコードに起因するものではない場合は、try
except
ブロックを使用することが、コードのフローを制御するための最善の策です。
例外は怖い必要はありません。 トレースバックで提供された情報と例外の根本原因を理解する方法がわかったら、これらのソリューションを使用して、プログラムの流れをより予測可能にすることができます。