Pythonの例外処理(try, except, else, finally)

Pythonで例外(実行中に検出されたエラー)をキャッチして処理するにはtry, exceptを使います。例外が発生しても途中で終了させずに処理を継続できます。さらにelse, finallyを使うことで終了時の処理を設定することができます。

  1. エラーと例外 - 例外を処理する — Python 3.7.1rc1 ドキュメント

ここでは以下の内容について説明します。

例外処理の基本: try, except 複数の例外をキャッチ 複数の例外に異なる処理を実行 複数の例外に同じ処理を実行 すべての例外をキャッチ ワイルドカードのexcept節(bare except) 基底クラスException 正常終了時の処理: else 終了時に常に行う処理: finally 例外を無視: pass

Pythonの主なエラーメッセージとその対策のまとめについては

例外処理の基本: try, except

例えばゼロによる除算が行われるとZeroDivisionErrorという例外が発生して処理が終了します。

# print(1 / 0)
# ZeroDivisionError: division by zero

この例外をキャッチ(捕捉)するには以下のように記述します。

try:
    print(1 / 0)
except ZeroDivisionError:
    print('Error')
# Error

さらに、except 例外型 as 変数名:とすることで、変数に例外オブジェクトを格納して使用することができます。この変数名にはeやerrなどの名前が使われることが多い。 例外オブジェクトには例外発生時に出力されるエラーメッセージなどが格納されているのでそれを出力することでエラーの内容を確認できます。

try:
    print(1 / 0)
except ZeroDivisionError as e:
    print(e)
    print(type(e))
# division by zero
# <class 'ZeroDivisionError'>

なお、except 例外型 as 変数名:はPython3での書き方で、Python2ではexcept 例外型, 変数名:と書きます。 Python組み込みの例外型の一覧は以下の公式ドキュメントを参照。

組み込み例外 — Python 3.7.1rc1 ドキュメント

try節で例外が発生した時点でtry節の中のそれ以降の処理はスキップされます。以下の例のようにforループの途中で例外が発生するとその時点でforループは終了してexcept節の処理に移行します。

try:
    for i in [-2, -1, 0, 1, 2]:
        print(1 / i)
except ZeroDivisionError as e:
    print(e)
# -0.5
# -1.0
# division by zero

正常終了時の処理や終了時に必ず行う処理などは後述のelse節やfinally節で指定します。

複数の例外をキャッチ

除算を行いZeroDivisionErrorをキャッチする以下の関数を定義します。

def divide(a, b):
    try:
        print(a / b)
    except ZeroDivisionError as e:
        print('catch ZeroDivisionError:', e)

ZeroDivisionErrorはキャッチできるが、それ以外の例外はキャッチできず処理が途中終了します。

divide(1, 0)
# catch ZeroDivisionError: division by zero

# divide('a', 'b')
# TypeError: unsupported operand type(s) for /: 'str' and 'str'

複数の例外をキャッチして処理を実行する方法を説明します。

複数の例外に異なる処理を実行

except節は複数指定することができます。複数の例外に対してそれぞれ異なる処理を記述できます。

def divide_each(a, b):
    try:
        print(a / b)
    except ZeroDivisionError as e:
        print('catch ZeroDivisionError:', e)
    except TypeError as e:
        print('catch TypeError:', e)

divide_each(1, 0)
# catch ZeroDivisionError: division by zero

divide_each('a', 'b')
# catch TypeError: unsupported operand type(s) for /: 'str' and 'str'

複数の例外に同じ処理を実行

一つのexcept節に複数の例外型をタプルで指定することもできます。 変数に格納される例外オブジェクトの中身はそれぞれ異なるので、エラーメッセージを出力してエラーの内容を確認するだけであればこれで問題ない。

def divide_same(a, b):
    try:
        print(a / b)
    except (ZeroDivisionError, TypeError) as e:
        print(e)

divide_same(1, 0)
# division by zero

divide_same('a', 'b')
# unsupported operand type(s) for /: 'str' and 'str'

すべての例外をキャッチ

基本的には想定される例外型をexcept節に指定すべきだが、特定の例外を指定せずにすべての例外をキャッチすることもできます。

ワイルドカードのexcept節(bare except)

except節から例外型を省略するとすべての例外をキャッチできます。複数のexcept節がある場合は例外型を省略できるのは最後のexcept節のみ。 例外型を省略した書き方はワイルドカードのexcept節やbare exceptなどと呼ばれるが、公式ドキュメントにあるように使用には注意が必要。

最後の except 節では例外名を省いて、ワイルドカード (wildcard、総称記号) にすることができます。ワイルドカードの except 節は非常に注意して使ってください。というのは、ワイルドカードは通常のプログラムエラーをたやすく隠してしまうからです! 8. エラーと例外 — Python 3.7.1rc1 ドキュメント

この方法では例外オブジェクトを取得できません。

def divide_wildcard(a, b):
    try:
        print(a / b)
    except:
        print('Error')

divide_wildcard(1, 0)
# Error

divide_wildcard('a', 'b')
# Error

基底クラスException

システム終了以外のすべての組み込み例外の基底クラスであるExceptionを使う方法もあります。

組み込み例外 Exception — Python 3.7.1rc1 ドキュメント

この方法では例外オブジェクトを取得できます。

def divide_exception(a, b):
    try:
        print(a / b)
    except Exception as e:
        print(e)

divide_exception(1, 0)
# division by zero

divide_exception('a', 'b')
# unsupported operand type(s) for /: 'str' and 'str'

なお、想定していない例外までキャッチしてしまうのはバグの温床になるので、繰り返しになるが、except節には可能な限り想定される例外型を指定したほうがいい。

正常終了時の処理: else

try節で例外が発生せず正常終了したあとに行う処理をelse節に指定できます。例外が発生してexceptでキャッチした場合はelse節の処理は実行されません。

def divide_else(a, b):
    try:
        print(a / b)
    except ZeroDivisionError as e:
        print('catch ZeroDivisionError:', e)
    else:
        print('finish (no error)')

divide_else(1, 2)
# 0.5
# finish (no error)

divide_else(1, 0)
# catch ZeroDivisionError: division by zero

終了時に常に行う処理: finally

例外が発生した場合もしなかった場合も常に最後に行う処理をfinally節に指定できます。

def divide_finally(a, b):
    try:
        print(a / b)
    except ZeroDivisionError as e:
        print('catch ZeroDivisionError:', e)
    finally:
        print('all finish')

divide_finally(1, 2)
# 0.5
# all finish

divide_finally(1, 0)
# catch ZeroDivisionError: division by zero
# all finish

else節とfinally節を同時に使うこともできます。正常終了時はelse節の処理が実行されたあとにfinally節の処理が実行されます。

def divide_else_finally(a, b):
    try:
        print(a / b)
    except ZeroDivisionError as e:
        print('catch ZeroDivisionError:', e)
    else:
        print('finish (no error)')
    finally:
        print('all finish')

divide_else_finally(1, 2)
# 0.5
# finish (no error)
# all finish

divide_else_finally(1, 0)
# catch ZeroDivisionError: division by zero
# all finish

例外を無視: pass

例外をキャッチしても特に何も処理を行わずにスルーしたい場合はpass文を使います。

def divide_pass(a, b):
    try:
        print(a / b)
    except ZeroDivisionError:
        pass

divide_pass(1, 0)

pass文は何もしない文。

Last Updated: 6/26/2019, 10:34:03 PM