この記事では、Pythonの例外処理について解説していきます。
Pythonの例外処理の概要
- Pythonの例外処理は、try、except、finallyの3つのキーワードで実現されます。
- try ブロックには、例外やエラーを発生させる可能性のあるコードを入れます。
- except ブロックは、例外をキャッチして処理するために使用されます。
- catchブロックのコードは、対応する例外が発生したときのみ実行されます。
- キャッチブロックは複数個存在することができます。また、1つのcatchブロックで複数の例外をキャッチすることもできます。
- finallyブロックのコードは、プログラムが正しく実行されたか、例外が発生したかにかかわらず、常に実行されます。
-
try-exceptブロックにelseブロックを作成することもできます。
elseブロック内のコードは、例外が発生しなかった場合に実行されます。
Pythonで例外を処理するには?
まずは以下の割り算の例を見ましょう。
def divide(x, y):
print(f'{x} / {y} is {x / y}')
divide(10 , 2)
divide(10 , 0)
divide(10 , 4)
実行すると、以下の様にエラーが発生するはずです。
Traceback (most recent call last):
File "Main.py", line 5, in
divide(10 , 0)
File "Main.py", line 2, in divide
print(f'{x} / {y} is {x / y}')
ZeroDivisionError: division by zero
2回目のdivide()関数の呼び出しでZeroDivisionError例外が発生し、プログラムが終了しました。
コード内で例外処理を行わなかったため、divide()メソッドの3回目の呼び出しの出力を得ることはできませんでした。
では、divide()メソッドを例外処理付きで書き換えてみましょう。
もし、0 で割ろうとしたら、例外をキャッチしてエラーメッセージを表示するようにします。
こうすれば、プログラムが早期に終了することはなく、出力もより意味のあるものになります。
def divide(x, y):
try:
print(f'{x}/{y} is {x / y}')
except ZeroDivisionError as e:
print(e)
divide(10 , 2)
divide(10 , 0)
divide(10 , 4)
出力は以下の通りです。
10/2 is 5.0
division by zero
10/4 is 2.5
Pythonの例外におけるBaseExceptionクラスとは?
BaseExceptionクラスは、すべての例外の基本クラスで,以下の4つのサブクラスがあります。
- Exception – すべての非実行例外の基本クラスです。
- GeneratorExit – ジェネレーターの終了を要求します。
- KeyboardInterrupt – ユーザーによって中断されたプログラム。
- SystemExit – インタプリタからの終了要求。
Pythonの色々な組み込み例外クラス
Pythonの組み込み例外クラスには、以下のようなものがあります。
-
ArithmeticError 算術エラーの基本クラスです。
-
AssertionError アサーションが失敗したときに発生します。
-
AttributeError 属性が見つからないときに発生します。
-
BufferError(バッファエラー
-
EOFError ファイルの終わりから読み込む。
-
ImportError インポートされたモジュールが見つからない場合。
-
LookupError ルックアップ・エラーの基本例外。
-
MemoryError メモリ不足が発生したとき。
-
NameError グローバルに名前が見つからない場合。
-
OSError I/Oエラーの基本クラス
-
リファレンスエラー
-
ランタイムエラー(RuntimeError
-
StopIteration 、 StopAsyncIteration 、 StopAsyncIteration 、 StopAsyncIteration 。
-
構文エラー – 構文が無効です。
-
システムエラー Pythonインタプリタの内部エラーです。
-
TypeError 引数の型が無効です。
-
ValueError 引数の値が無効です。
いくつかの組み込み型 Warningクラス
Warningクラスは、すべてのWarningの基本クラスです。
以下のサブクラスがあります。
-
BytesWarning – バイトとバッファに関する警告で、主に文字列の変換と比較に関連するものです。
-
DeprecationWarning – 非推奨の機能に関する警告。
-
FutureWarning – 将来的にセマンティックに変更される構成についての警告のための基本クラスです。
-
ImportWarning – モジュールのインポートにおける間違いについての警告。
-
PendingDeprecationWarning – 将来非推奨となる機能についての警告です。
-
ResourceWarning – リソース使用に関する警告
-
RuntimeWarning – ランタイムの怪しげな動作に関する警告です。
-
SyntaxWarning – 怪しげな構文に関する警告。
-
UnicodeWarning – Unicode 変換に関連する警告です。
-
UserWarning – ユーザーコードによって生成される警告
1つのExceptブロック内で複数の例外を処理する
Pythonのtryブロックは、複数のexceptブロックを持つことができます。
それぞれのexceptブロックで特定の例外をキャッチすることができます。
def divide(x, y):
try:
print(f'{x}/{y} is {x / y}')
except ZeroDivisionError as e:
print(e)
except TypeError as e:
print(e)
except ValueError as e:
print(e)
どのexceptブロックのコードも同じです。
上記のコードでは、1つのexceptブロックの中で複数の例外を処理することができます。
例外オブジェクトのタプルをexceptブロックに渡すことで、複数の例外をキャッチすることができる。
この記事もチェック:Pythonのmapメソッドの使い方|ラムダ関数や引数が複数の場合の使い方も解説
単一のExceptブロックで全ての例外を捕捉する
except ブロックで例外クラスを指定しなければ、try ブロックで発生した例外をすべて捕捉します。
try ブロックが発生させることができる例外がわからない場合に、これを指定することは有益です。
空のexcept節は例外処理の連鎖の最後の1つでなければなりません。
def divide(x, y):
try:
print(f'{x}/{y} is {x / y}')
except ZeroDivisionError as e:
print(e)
except:
print("unknown error occurred")
elseブロックと try-exceptを併用して使う
elseブロックのコードは任意です。
try ブロックで発生した例外がない場合に実行されます。
def divide(x, y):
try:
print(f'{x}/{y} is {x / y}')
except ZeroDivisionError as e:
print(e)
else:
print("divide() function worked fine.")
divide(10 , 2)
divide(10 , 0)
divide(10 , 4)
try-exceptでfinallyブロックを使用する
finallyブロックのコードは、例外の有無にかかわらず、すべてのケースで実行されます。
def divide(x, y):
try:
print(f'{x}/{y} is {x / y}')
except ZeroDivisionError as e:
print(e)
else:
print("divide() function worked fine.")
finally:
print("close all the resources here")
divide(10 , 2)
divide(10 , 0)
divide(10 , 4)
出力は以下の通りです。
10/2 is 5.0
divide() function worked fine.
close all the resources here
division by zero
close all the resources here
10/4 is 2.5
divide() function worked fine.
close all the resources here
例外クラスを自作して使う
Exceptionクラスを継承することで、独自の例外クラスを作成することができます。
ベストプラクティスは、ベースとなる例外を作成し、他の例外クラスを派生させることです。
ここでは、ユーザー定義の例外クラスを作成する例をいくつか紹介します。
class EmployeeModuleError(Exception):
"""Base Exception Class for our Employee module"""
pass
命名規則として、例外クラス名の末尾に “Error “を付けると良いです。
raiseキーワードを使って例外を発生させる
raise キーワードを使用すると、コードから例外をスローすることができます。
想定されるシナリオは以下の通りです。
- 関数入力パラメータの検証に失敗する
-
例外をキャッチしてからカスタム例外を投げる
try-exceptブロックの入れ子の例
Pythonではtry-exceptブロックを入れ子にすることができます。この場合、ネストされたtryブロックの中で例外が発生すると、ネストされたexceptブロックがその例外を処理するために使用されます。
例外を処理できない場合は、外側のexceptブロックが例外を処理するために使用されます。
x = 10
y = 0
try:
print("outer try block")
try:
print("nested try block")
print(x/y)
exceptTypeError as te:
print("nested except block")
print(te)
exceptZeroDivisionError as ze:
print("outer except block")
print(ze)
結果は以下の様になります。
outer try block
nested try block
outer except block
division by zero
Pythonの例外処理のベストプラクティス
以下は、Pythonで例外処理を行う際のベストプラクティスをまとめていきます。
- 例外クラスを自作する場合、名前の末尾に “Error”を付ける。
- except節に同じコードがある場合は、1つのexceptブロックで複数の例外をキャッチするようにします。
- ファイルやデータベースのリソースをcloseするために、finallyブロックを使用します。
- elseブロックは、コードの実行が成功したことを記録したり、通知を送ったりするために使用します。
- 裸のexcept節はできるだけ避ける。
- 例外のメッセージは、どんな例外が発生したかが明確な内容にします。
- try-exceptブロックのネストは、コードの可読性を低下させるので避ける。
この記事もチェック:Pythonで自作の例外を作ってraiseとか終了処理を行う方法