ここまでプログラム実行時に発生した例外の処理を見てきたが、raise文を使用することで、任意の例外を任意の時点で送出できる。
最も分かりやすい例を以下に示す(これまでと同様、[Interactive]ウィンドウでの実行結果)。
>>> raise RuntimeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError
このとき、コンストラクタにエラーメッセージを引き渡すこともできる(強調書体部分に注目)。
>>> raise RuntimeError('hello from runtime error')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: hello from runtime error
例外処理時に、その例外を適切に処理できなかったときには単に「raise」とするだけで、例外が再送出される。以下に例を示す。ここでは関数funcが例外を処理せずに、再送出するようにしている。また、関数callerから関数funcを呼び出すことを想定している。関数callerでは例外処理をしていないことに注意。
def func(exp):
try:
return eval(exp)
except ZeroDivisionError:
print('division by zero')
raise
except NameError as e:
print('undefined name')
raise
except Exception as e:
print('some exception!', e)
raise RuntimeError(e.args) from e
def caller():
x = input('input expression:')
print(func(x))
最後の「except Exception from e」行では、発生した例外からさらに例外を発生させている(例外の連鎖)。このときには、新たに発生した例外の原因を「from」に続けて記述する。
実行例を以下に示す。なお、ここでは上のコードをexceptionsample.pyファイルに保存し、これを[Interactive]ウィンドウでインポートしている([Interactive]ウィンドウでうまく動かないときにはVisual Studioを再起動するか、コマンドラインのpythonコマンドで対話環境を起動するなどしてみてほしい)。
>>> from exceptionsample import func, caller
>>> caller() # 例外の再送出
input expression:
1/0
division by zero
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".\exceptionsample.py", line 16, in caller
print(func(x))
File ".\exceptionsample.py", line 3, in func
return eval(exp)
File "<string>", line 1, in <module>
ZeroDivisionError: division by zero
>>> caller() # 例外の連鎖
input expression:
1 + str(100)
some exception! unsupported operand type(s) for +: 'int' and 'str'
Traceback (most recent call last):
File ".\exceptionsample.py", line 3, in func
return eval(exp)
File "<string>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".\exceptionsample.py", line 16, in caller
print(func(x))
File ".\exceptionsample.py", line 12, in func
raise RuntimeError(e.args) from e
RuntimeError: ("unsupported operand type(s) for +: 'int' and 'str'",)
最初の例(「1/0」)では、exceptionsample.pyファイルの3行目(「eval(exp)」行)で発生した例外がそのまま再送出されている。一方、2つ目の例では、同様に3行目で発生したTypeError例外を直接の原因としてRuntimeError例外が送出されたことが分かる(スタックトレースが2つあり、その後者の「raise RuntimeError(e.args) from e」でRuntimeError例外が送出されたことが分かる)。関数caller側では元の関数funcと同様にして再送出された例外を処理できる(コードは割愛)。
独自の例外クラスも作成できる。以下に簡単な例を示す。
class MyException(Exception):
def __init__(self, message):
self.message = message
try:
raise MyException('hello')
except MyException as e:
print(e)
ポイントはExceptionクラスまたはその派生クラスを、独自クラスの基底クラスとすることだ。Pythonではユーザー定義の例外クラスはExceptionクラスの派生クラスであることが必要となっているので注意しよう(実行結果は割愛)。
本稿では、Pythonの例外の基礎について見てきた。Pythonでは例外がtry〜except〜else〜finally文で取り扱う。exceptではキャッチする例外を指定したり、例外オブジェクトを取得したりできる。else節はtry節で例外が発生されなかったときに実行され、finally節はtry節での例外の有無に関係なく実行される。各節がどのような順序で実行されるかは(本稿では触れなかったが、例外の再送出が絡んだ場合にはより一層の)注意が必要だ。次回は、落ち穂拾い的にさまざまな要素を取り上げていくことにしよう。
Copyright© Digital Advantage Corp. All Rights Reserved.