【Python】例外処理の書き方。raise, try, except【入門第40回】

144, 2019-09-01

目次

例外とは?

こんにちは、narupoです。

今回はPythonの例外についてやっていきます。
例外はエラーが発生したときに送出されるものです。
この記事では例外を送出する方法と、送出した例外を捕まえる方法を解説します。

具体的には↓を見ていきます。

  • 例外が発生するケース

  • raise文。例外を手動で発生させる

  • try文。例外を捕まえる

例外が発生するケース

例外はどういったときに送出されるのでしょうか。
Pythonではエラーが起きたときに例外が送出されます。
たとえば0除算エラーがそうです。

0除算エラーとは、割り算では0で割ることが出来ないので、Pythonインタプリタは0で除算する命令があるとZeroDivisionErrorという例外を送出します。

たとえば↓のようなコード。

:::python
1 / 0

これを実行すると↓のような結果になります。

ZeroDivisionError: division by zero

Zero0で、Divisionは除算という意味です。
0で除算された! というエラー内容ですね。
このZeroDivisionErrorというのは例外で使われる組み込みのクラス名です。
組み込みのクラスなので、↓のようにインスタンス化することも可能です。

:::python
e = ZeroDivisionError()

例外のための組み込みクラスは、このほかにも沢山あります。
それらはすべて利用することが可能です。
例外クラスの一覧は組み込み例外 — Python 3.7.4 ドキュメントから確認できます。
これらの例外は用途別に違う名前が付けられています。
0除算エラーならZeroDivisionError, ファイルが見つからなければFileNotFoundErrorなど。
ひと通り目を通しておくと、どんな例外が用意されているかわかると思います。

数が多いのでめんどくさい

それな

raise文。例外を手動で発生させる

例外を手動で送出するにはraise文を使います。
raise文は↓のように使います。

:::text
raise 例外クラス

組み込み例外クラスにExceptionというクラスがあるのでこれを送出してみましょう。

:::python
raise Exception('本能寺が炎上しました')

↑のコードの実行結果は↓のようになります。

Traceback (most recent call last):
  File "sample.py", line 1, in <module>
    raise Exception('本能寺が炎上しました')
Exception: 本能寺が炎上しました

↑の最後の行は例外の内容を表しています。
Tracebackというのは、例外がどのように送出されたかの記録です。
↑の場合は「ファイルsample.pyの1行目で例外が発生した」ということがわかります。
これをスタックトレースと言います。

例外クラスのコンストラクタの第1引数には、例外の内容を書きます。
↓の場合は「本能寺が炎上した」というエラー内容ですね。

:::python
raise Exception('本能寺が炎上しました')

例外の送出範囲

送出された例外はどこまでも飛んでいきます。
どういうことかと言うと、例えば↓のような関数があるとします。

:::python
def func():
    raise Exception('例外が発生しました')
    print('このprintは呼ばれません')

func()

raiseで送出された例外はfuncを飛び越えて送出されます。
例外が送出された時点で、それまでの処理はストップします。
↑の例では

:::python
print('このprintは呼ばれません')

↑のprintは実行されません。

最終的にキャッチされない例外は、スタックトレースを出力してプログラムを異常終了させます。
これは、関数が何層になっていても同じことです。
たとえば↓のコードを見てください。

:::python
def f3():
    raise Exception('f3で例外が発生しました')

def f2():
    f3()

def f1():
    f2()

f1()

↑のコードの実行結果は↓のようになります。

Traceback (most recent call last):
  File "sample.py", line 10, in <module>
    f1()
  File "sample.py", line 8, in f1
    f2()
  File "sample.py", line 5, in f2
    f3()
  File "sample.py", line 2, in f3
    raise Exception('f3で例外が発生しました')
Exception: f3で例外が発生しました

↑のTracebackのスタックトレースを見るとわかりますが、f3で送出された例外はf3を飛び越えて、f2に来ます。
そしてf2を飛び越えてf1に、f1からプログラムの最表面に来ます。

このように例外はキャッチしない限りどこまでも送出されて、最終的にプログラムをストップさせます。

try文。例外を捕まえる

送出された例外をキャッチ(捕まえる)するにはtry文を使います。
try文は↓のように使います。

:::text
try:
    例外が発生するであろうコード
except 例外クラス:
    例外をキャッチした場合のコード

たとえば↓を見てください。

:::python
try:
    raise Exception('へそくりが見つかりませんでした')
except Exception:
    print('例外を捕まえました')

try文で例外を捕まえるには、↑のようにtry ~ exceptで例外が発生するであろうコードを囲みます。
exceptには捕まえたい例外クラスを書きます。
↑の場合はExceptionを捕まえたいのでexcept Exception:と書いています。
↑のコードの実行結果は↓のようになります。

例外を捕まえました

このように送出された例外をtry文で捕まえると、例外の送出はその時点でストップします。
ストップするので、プログラムも異常終了せずにスタックトレースも出力されません。

例外のインスタンスを参照する

さきほどの例外は↓のような内容でした。

:::python
raise Exception('へそくりが見つかりませんでした')

この例外のインスタンスを参照したい場合は、↓のようにします

:::python
try:
    raise Exception('へそくりが見つかりませんでした')
except Exception as e:
    print(e)

except Exception as e:のように、例外クラスのあとにasをつけてインスタンス名(e)を書きます。
インスタンス名はeじゃなくても、たとえばerrなどにしても大丈夫です。
そしてこのインスタンスeを、print関数で出力しています。
↑のコードの実行結果は↓のようになります。

へそくりが見つかりませんでした

例外の内容を参照できていますね。
インスタンスをstrクラスに渡すとエラー内容を文字列として取得できます。

:::python
what = str(e)

複数の例外に対応する

プログラムにはいろいろなエラーがあります。
開こうとしたファイルが見つからなかったり、メモリーが足りなかったり、いろいろなケースがありえます。
例外を捕まえる場合、その複数のエラーに対応できないと不便です。

try文では複数の例外に対応することができます。
たとえば↓のようにです。

:::python
try:
    func()
except ZeroDivisionError:
    print('0除算エラーが発生しました')
except FileNotFoundError:
    print('ファイルが見つかりませんでした')

↑のようにexcepttry文の後ろにくっつけていくと、複数の例外の送出に対応できます。
たとえば↑のfunc内でZeroDivisionErrorが送出されると、except ZeroDivisionError:に来ます。
FileNotFoundErrorが送出された場合はexcept FileNotFoundError:にです。

このように複数の例外の送出に対応できると何が嬉しいのかと言うと、例外の内容に応じたエラー処理を書くことができるのです。
0除算エラーが発生したらプログラムを終了させて、ファイルが見つからなかったらユーザーに再びファイル名の入力を促すとか、柔軟な対応が可能になります。

複数の例外をまとめる

カッコ(())で囲うと、1つのexceptで複数の例外をまとめることができます。
たとえば↓のようにです。

:::python
try:
    func()
except (ZeroDivisionError, FileNotFoundError):
    print('私はパニックです。帰らせていただきます')

↑の場合、funcからZeroDivisionErrorFileNotFoundErrorが送出されると「私はパニックです。帰らせていただきます」と表示されます。

もちろんインスタンスも参照できます。
インスタンスを参照するには↓のようにします。

:::python
try:
    func()
except (ZeroDivisionError, FileNotFoundError) as e:
    print(e)

おわりに

例外には他にも機能があります。
次回はそれについてご紹介したいと思います。

以上、次回に続きます。

また見てね

関連動画