例外

【Python】例外処理の基本(exception)

【Python】例外処理の基本(exception)

Pythonの例外処理(exception)の基本について解説します。

例外処理(exception)

例外(exception)は、プログラムの実行中に発生し、処理を中断させる原因となる問題のことです。多くのプログラミング言語と同様に、Pythonにも例外処理の仕組みがあります。

例外が発生した場合、適切な対応を行い、プログラムの継続や安全な終了を考えることが重要です。例外を処理するコードは「例外ハンドラ」と呼ばれ、例外をキャッチしない場合は上位の関数に伝わり、適切なハンドラが見つかるまで伝播します。

適切な例外ハンドラがない場合、Pythonはエラーメッセージと発生場所を表示し、プログラムを強制終了します。例えば、以下のコードではインデックスエラー(IndexError)が発生します。

data_list = ["A", "B", "C"]
print(data_list[5])
【実行結果例】
Traceback (most recent call last):
  File "D:/~/exception_sample.py", line 2, in <module>
    print(data_list[5])
IndexError: list index out of range

Process finished with exit code 1

上記例では、インデックス範囲外のアクセスでエラーが発生し、プログラムが強制終了しています。例外が起きた際には、プログラマが適切な処理を記述してプログラムを「継続」または「終了」させることが必要になります。

この記事では、Pythonの例外処理の基本的な使い方について紹介します。

例外処理の基本的な方法

ここからは、例外処理の基本について説明します。

例外のキャッチ(tryexcept

使い方の基本

例外が発生する可能性があるコードをtryで囲み、exceptで例外をキャッチします。以下の例で基本的な使い方を確認しましょう。

data_list = ["A", "B", "C"]
try:
    print(data_list[5])
except IndexError as ex:
    print(ex)
print("end")
【実行結果】
list index out of range
end

上記例では、print(data_list[5])IndexErrorをスローし、exceptブロックでキャッチされます。print(ex)によりエラーメッセージが出力され、その後のprint("end")も実行されます。プログラムは終了せずに次の処理へ進みます。

asで例外を変数に受け取りますが変数名については特に決まりはありません。exeなどがよく使用されます。受け取った例外exを確認することで例外の詳細を確認することができます。

例外が発生したらプログラムを終了する

例外が発生した場合、プログラムを終了することも可能です。

import sys

data_list = ["A", "B", "C"]
try:
    print(data_list[5])
except IndexError as ex:
    print(ex)
    sys.exit(1)
print("end")
【実行結果】
list index out of range

Process finished with exit code 1

上記例では、例外が発生したら例外を表示して、sys.exitで終了しています。このように、必要な処理を行ってから安全にプログラムを終了できます。

複数の例外のキャッチ

Pythonには、多くの例外クラスが用意されています。Pythonドキュメントの「例外のクラス階層」で組み込みの例外クラス階層を確認できます。

Pythonプログラミングでは、使用するモジュールによって発生する例外が異なるため、複数の例外が発生する可能性があります。Pythonでは、exceptを列挙することで複数の例外をキャッチすることができます。

data_list = ["A", "B", "C"]

while True:
    input_value = input("input index (q to quit): ")

    if input_value == "q":
        break

    try:
        index = int(input_value)
        print(data_list[index])
    except IndexError as ex:
        print(f"bad index : {ex}")
    except ValueError as ex:
        print(f"bad value : {ex}")
【実行結果】
input index (q to quit): 0
A
input index (q to quit): 1
B
input index (q to quit): 2
C
input index (q to quit): 3
bad index : list index out of range
input index (q to quit): one
bad value : invalid literal for int() with base 10: 'one'
input index (q to quit): q

上記例では、リストのインデックスが範囲外の場合は、IndexErrorが、型変換できない値が入力された場合はValueErrorがスローされ、それぞれのexceptでキャッチされています。このように複数の例外を列挙して処理できます。

独自例外の作成と例外のスロー(raise

独自の例外を作成して処理したい場合は、例外クラスを作成し、raiseでスローすることが可能です。以下は、独自の例外を作成する例です。

class MyException(Exception):
    pass


def my_original_method(value):
    if value < 0:
        raise MyException("minus value is not allowed")
    else:
        return value


def main():
    value = -10
    try:
        print(my_original_method(value))
    except MyException as ex:
        print(ex)


if __name__ == "__main__":
    main()
【実行結果】
minus value is not allowed

上記例では、MyExceptionクラスをExceptionから継承して作成し、my_original_method関数内で負の数値が渡された場合に例外をスローしています。

例外のスローの際には、エラーメッセージをraiseに渡して使用します。exceptで独自例外をキャッチすることで、例外処理が可能です。

Pythonの例外は、BaseExceptionクラスから派生したクラスである必要があり、一般的には、Exceptionクラスを継承して独自例外を作成します。例外のクラス階層については、Pythonドキュメントの「例外のクラス階層」を参照してください。

例外処理での注意事項

except Exceptionで全ての例外をキャッチすべきではない

例外処理では「except Exception」で全ての例外をキャッチすることは避けるべきです。以下は、上述のプログラムを「except Exception」に書き換えた例です。

data_list = ["A", "B", "C"]

while True:
    input_value = input("input index (q to quit): ")

    if input_value == "q":
        break

    try:
        index = int(input_value)
        print(data_list[index])
    except Exception as ex:
        print(f"exception: {ex}")
【実行結果】
input index (q to quit): 0
A
input index (q to quit): 1
B
input index (q to quit): 2
C
input index (q to quit): 3
exception: list index out of range
input index (q to quit): one
exception: invalid literal for int() with base 10: 'one'
input index (q to quit): q

このプログラムでは「except Exception」が全ての例外をキャッチしています。except Exceptionは、特定の例外に限らず全ての例外をまとめてキャッチできるため、プログラムは動作しますが、この方法は推奨されません。

理由として「想定外の例外に対する処理ほど危険なものはない」とされるからです。プログラマは、自分のプログラムで発生しうる例外を洗い出し、それに対する処理を明確に記載するべきです。これはプログラム品質向上に不可欠な作業です。

全ての例外を想定することは難しいため、必要な例外を個別にキャッチした後に「except Exception」でそのほかを処理するのはやむを得ない場合もあります。しかし、何が発生するかわからないからと言って安易に全ての例外をキャッチすることは適切ではありません。

このことは、Pythonに限らずプログラマのとして心構えとして重要です。

まとめ

Pythonの例外処理(exception)の基本として、try, except, raiseの使い方、複数の例外処理、独自例外作成について解説しました。また、「exept Exception」で全ての例外を安易にキャッチすることは推奨されないことも説明しています。

例外が発生した場合、適切な通知やプログラム停止など、プログラマが対応を十分に考慮することが重要です。例外処理を適切に記述し、利用者に何が起きたかを知らせることで、安全で高品質なプログラム開発を目指しましょう。