2021-09-10

Pythonの例外処理 "finally", "from" についてメモ

Python の例外処理の基礎的な部分を再確認した。

Python: 3.9.7 を使用。

finally がある場合の実行順序

もし finally 節がある場合、 try 文が終わる前の最後の処理を、 finally 節が実行します。 8. エラーと例外 — Python 3.10.4 ドキュメント

def raise_error():
    raise Exception(3)

try:
    raise_error()
except Exception as e:
    print(1)
    raise e
finally:
    print(2)

出力:

1
2
Traceback (most recent call last):
  File "/Users/noy72/sandbox/python/error/finally.py", line 9, in <module>
    raise e
  File "/Users/noy72/sandbox/python/error/finally.py", line 6, in <module>
    raise_error()
  File "/Users/noy72/sandbox/python/error/finally.py", line 2, in raise_error
    raise Exception(3)
Exception: 3

実行順序は以下のようになっている。

  1. except節のprint(1)
  2. finally節のprint(2)
  3. except節のraise e

finally節が実行された後に例外(raise e)再送されるので、↑の順で実行される。

finally節の実行前に関数から抜けたらfinally節の意味がないのでこの実行順序になるのは当たり前だけど、直感に反する感じがする。


raise ... from のエラーメッセージの変化

class SomeException(Exception):
    pass

def raise_error():
    raise Exception("エラーが発生しました")

try:
    raise_error()
except Exception as e:
    raise SomeException

上記のコードを実行した場合の実行結果は以下のようになる。ExceptionSomeExceptionで投げ直しているが、Exceptionがどこで発生したかの情報も含まれている。

Traceback (most recent call last):
  File "/Users/noy72/sandbox/python/error/raise_from.py", line 10, in <module>
    raise_error()
  File "/Users/noy72/sandbox/python/error/raise_from.py", line 6, in raise_error
    raise Exception("エラーが発生しました")
Exception: エラーが発生しました

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/noy72/sandbox/python/error/raise_from.py", line 12, in <module>
    raise SomeException
__main__.SomeException

当然だけど、raise SomeException(e)にするとexcept節で発生した例外のメッセージをSomeExceptionも持つことになる。

(略)

Traceback (most recent call last):
  File "/Users/noy72/sandbox/python/error/raise_from.py", line 12, in <module>
    raise SomeException(e)
__main__.SomeException: エラーが発生しました

raise SomeException from eにすると、表示される文字列が変わる。

(略)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/noy72/sandbox/python/error/raise_from.py", line 12, in <module>
    raise SomeException from e
__main__.SomeException

fromがない場合はDuring handling of the above exception, another exception occurred:で、fromがある場合はThe above exception was the direct cause of the following exception:となる。

例外の連鎖は、例外が except 節または finally 節で送出された場合自動的に行われます。例外の連鎖を無効にするためには from None が利用できます:

8. エラーと例外 — Python 3.10.4 ドキュメント

fromがあるときとないときでスタックトレースに変化がないのは例外の連鎖が有効なため。明示的にfromを使う場面があまりなさそうな気がする。


raise SomeException from Noneと書いて実行すると以下のトレースが表示される。raise_error()で発生したという情報がなくなる。

Traceback (most recent call last):
  File "/Users/noy72/sandbox/python/error/raise_from.py", line 12, in <module>
    raise SomeException from None
__main__.SomeException