note.nkmk.me

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

Date: 2018-09-27 / tags: Python

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

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

  • 例外処理の基本: 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 変数名:とすることで、変数に例外オブジェクトを格納して使用することができる。この変数名にはeerrなどの名前が使われることが多い。

例外オブジェクトには例外発生時に出力されるエラーメッセージなどが格納されているのでそれを出力することでエラーの内容を確認できる。

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

なお、except 例外型 as 変数名:はPython3での書き方で、Python2ではexcept 例外型, 変数名:と書く。

Python組み込みの例外型の一覧は以下の公式ドキュメントを参照。

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を使う方法もある。

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

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文は何もしない文。詳細は以下の記事を参照。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事