14. 例外#
14.1. 例外の捕捉#
以下のコードは数値を\(0\)で割るため、実行するとエラー(例外)が発生する。
2. / 0
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
/tmp/ipykernel_205130/4032807262.py in <module>
----> 1 2. / 0
ZeroDivisionError: float division by zero
プログラム中でエラーが発生したときは、例外クラスのオブジェクトが作成・送出される。例えば、上の例ではZeroDivisionErrorという例外クラスのオブジェクトが'float division by zero'
というメッセージ付きで送出されている。
プログラムの実行中に例外が発生するとPythonインタプリタの実行が直ちに停止されるが、 try文およびexcept節を用いて、例外をプログラム中で捕捉することも可能である。
try:
2. / 0
except ZeroDivisionError as e:
print('ゼロ除算:', e)
ゼロ除算: float division by zero
try文を用いても、except節で指定した例外クラス以外の例外が発生した場合は、except節は実行されずPythonインタプリタの実行が止まってしまう。以下のプログラムで発生する例外はOverflowErrorであるため、ZeroDivisionErrorに関するexcept節では捕捉しきれない。
try:
2. ** 1024
except ZeroDivisionError as e:
print(e)
---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
/tmp/ipykernel_205130/1994322563.py in <module>
1 try:
----> 2 2. ** 1024
3 except ZeroDivisionError as e:
4 print(e)
OverflowError: (34, 'Numerical result out of range')
except節を複数並べることで、異なる複数の例外に対する処理を記述できる。
try:
2. ** 1024
except ZeroDivisionError as e:
print('ゼロ除算:', e)
except OverflowError as e:
print('オーバーフロー:', e)
オーバーフロー: (34, 'Numerical result out of range')
try:
2. / 0
except ZeroDivisionError as e:
print('ゼロ除算:', e)
except OverflowError as e:
print('オーバーフロー:', e)
ゼロ除算: float division by zero
except節に例外クラスをタプルとしてまとめることで、異なる複数の例外に対する処理をまとめて記述できる。
try:
2. ** 1024
except (ZeroDivisionError, OverflowError) as e:
print('例外:', type(e), e)
例外: <class 'OverflowError'> (34, 'Numerical result out of range')
try:
2. / 0
except (ZeroDivisionError, OverflowError) as e:
print('例外:', type(e), e)
例外: <class 'ZeroDivisionError'> float division by zero
なお、ZeroDivisionErrorやOverflowErrorなどの組み込み例外の基底クラスはExceptionであるため、except節でExceptionクラスを指定すると、異なる種類の例外をまとめて捕捉できる。
try:
2. ** 1024
except Exception as e:
print('例外:', type(e), e)
例外: <class 'OverflowError'> (34, 'Numerical result out of range')
try:
2. / 0
except Exception as e:
print('例外:', type(e), e)
例外: <class 'ZeroDivisionError'> float division by zero
14.2. 例外の送出#
raise文を用いると、自分で実装したコードの中から例外を送出できる。
def timestamp(hour, minute, second):
if hour < 0 or 24 <= hour:
raise ValueError("引数hourは0 <= hour < 24を満たす必要があります")
if minute < 0 or 60 <= minute:
raise ValueError("引数minuteは0 <= minute < 60を満たす必要があります")
if second < 0 or 60 <= second:
raise ValueError("引数secondは0 <= second < 60を満たす必要があります")
return hour * 3600 + minute * 60 + second
timestamp(2, 43, 70)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/tmp/ipykernel_205130/3015373880.py in <module>
----> 1 timestamp(2, 43, 70)
/tmp/ipykernel_205130/1757576539.py in timestamp(hour, minute, second)
5 raise ValueError("引数minuteは0 <= minute < 60を満たす必要があります")
6 if second < 0 or 60 <= second:
----> 7 raise ValueError("引数secondは0 <= second < 60を満たす必要があります")
8 return hour * 3600 + minute * 60 + second
ValueError: 引数secondは0 <= second < 60を満たす必要があります
関数の呼び出し側にtry文を埋め込み、例外を捕捉することも可能である。
try:
timestamp(48, 0, 0)
except Exception as e:
print('例外:', type(e), e)
例外: <class 'ValueError'> 引数hourは0 <= hour < 24を満たす必要があります
例外クラスを自分で定義することもできる。ユーザ定義の例外クラスはExceptionを継承する必要がある。
class TimestampError(Exception):
pass
def timestamp(hour, minute, second):
if hour < 0 or 24 <= hour:
raise TimestampError("引数hourは0 <= hour < 24を満たす必要があります")
if minute < 0 or 60 <= minute:
raise TimestampError("引数minuteは0 <= minute < 60を満たす必要があります")
if second < 0 or 60 <= second:
raise TimestampError("引数secondは0 <= second < 60を満たす必要があります")
return hour * 3600 + minute * 60 + second
timestamp(1, 95, 22)
---------------------------------------------------------------------------
TimestampError Traceback (most recent call last)
/tmp/ipykernel_205130/3588777746.py in <module>
----> 1 timestamp(1, 95, 22)
/tmp/ipykernel_205130/1955752630.py in timestamp(hour, minute, second)
6 raise TimestampError("引数hourは0 <= hour < 24を満たす必要があります")
7 if minute < 0 or 60 <= minute:
----> 8 raise TimestampError("引数minuteは0 <= minute < 60を満たす必要があります")
9 if second < 0 or 60 <= second:
10 raise TimestampError("引数secondは0 <= second < 60を満たす必要があります")
TimestampError: 引数minuteは0 <= minute < 60を満たす必要があります
今回の引数チェックの場合、hour
, minute
, second
のそれぞれに対し、別の例外クラスを定義するのも一案である。
class TimestampError(Exception):
pass
class TimestampHourError(TimestampError):
pass
class TimestampMinuteError(TimestampError):
pass
class TimestampSecondError(TimestampError):
pass
def timestamp(hour, minute, second):
if hour < 0 or 24 <= hour:
raise TimestampHourError("引数hourは0 <= hour < 24を満たす必要があります")
if minute < 0 or 60 <= minute:
raise TimestampMinuteError("引数minuteは0 <= minute < 60を満たす必要があります")
if second < 0 or 60 <= second:
raise TimestampSecondError("引数secondは0 <= second < 60を満たす必要があります")
return hour * 3600 + minute * 60 + second
try:
timestamp(2, 68, 0)
except Exception as e:
print('例外:', type(e), e)
例外: <class '__main__.TimestampMinuteError'> 引数minuteは0 <= minute < 60を満たす必要があります
なお、デバッグ目的であればassert文を使い、引数の値をチェック(テスト)して、違反していたらAssertionErrorを送出することもできる。
def timestamp(hour, minute, second):
assert 0 <= hour < 24, "引数hourは0 <= hour < 24を満たす必要があります"
assert 0 <= minute < 60, "引数minuteは0 <= minute < 60を満たす必要があります"
assert 0 <= second < 60, "引数secondは0 <= second < 60を満たす必要があります"
return hour * 3600 + minute * 60 + second
timestamp(2, 43, 70)
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
/tmp/ipykernel_205130/3015373880.py in <module>
----> 1 timestamp(2, 43, 70)
/tmp/ipykernel_205130/811021604.py in timestamp(hour, minute, second)
2 assert 0 <= hour < 24, "引数hourは0 <= hour < 24を満たす必要があります"
3 assert 0 <= minute < 60, "引数minuteは0 <= minute < 60を満たす必要があります"
----> 4 assert 0 <= second < 60, "引数secondは0 <= second < 60を満たす必要があります"
5 return hour * 3600 + minute * 60 + second
AssertionError: 引数secondは0 <= second < 60を満たす必要があります
14.3. よく見かける例外#
文法エラー
2 / / 1
File "/tmp/ipykernel_205130/3671002116.py", line 1
2 / / 1
^
SyntaxError: invalid syntax
インデントの間違い
for i in range(10):
if i % 2 == 0:
print('even')
File "/tmp/ipykernel_205130/2214768420.py", line 3
print('even')
^
IndentationError: expected an indented block after 'if' statement on line 2
オーバーフロー
2. ** 1024
---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
/tmp/ipykernel_205130/997473611.py in <module>
----> 1 2. ** 1024
OverflowError: (34, 'Numerical result out of range')
未定義の変数
a * 2
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
/tmp/ipykernel_205130/188921875.py in <module>
----> 1 a * 2
NameError: name 'a' is not defined
範囲外のインデックス
x = [1, 2, 3]
x[4]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
/tmp/ipykernel_205130/3042274859.py in <module>
1 x = [1, 2, 3]
----> 2 x[4]
IndexError: list index out of range
キーが見つからない
y = {'one': 1, 'two': 2, 'three': 3}
y['four']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
/tmp/ipykernel_205130/1027427460.py in <module>
1 y = {'one': 1, 'two': 2, 'three': 3}
----> 2 y['four']
KeyError: 'four'
型の不整合
x + y
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_205130/978381659.py in <module>
----> 1 x + y
TypeError: can only concatenate list (not "dict") to list
ファイルが見つからない
open('hoge.txt')
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
/tmp/ipykernel_205130/1539795522.py in <module>
----> 1 open('hoge.txt')
FileNotFoundError: [Errno 2] No such file or directory: 'hoge.txt'