TL;DR
例外とは、エラーが起きたときに実行されるコードのことである。try 文を用いて例外が発生しそうなところに例外処理を追加し、ユーザーに何が起こるのか知らせる。
例外のポイント
- Python は例外の発生を検知すると強制的にプログラムを終了する
- 例外の情報とともにトレースバックと呼ばれる例外発生の箇所に関する情報が出力される
- 例外の補足には try 文を使う
- 例外が発生したときの処理は except 節に記述する
- 例外が発生しなかったときの処理は else 文に記述する
- 例外の発生有無に関係なく実行したい処理は finally 文を記述する
例外の種類
例外クラスは、BaseException という例外を継承している。
BaseException
└── Exception
├── ZeroDivisionError
├── AttributeError
├── TypeError
└── ValueError
次に示す例外は、すべて Exception のサブクラスである。
例外クラス | 意味 |
---|---|
IndexError | 存在しない範囲のインデックスを指定した |
AttributeError | 存在しない属性を指定した |
KeyError | マッピング型で存在しないキーを指定した |
TypeError | 不正な型を指定した |
ValueError | 不正な値を指定した |
ZeroDivisionError | 0 を除算した |
BaseException | すべての例外のスーパークラス |
try 文
try 文は、例外の補足を行う。
例外の発生が事前に予期できる場合は、try 文を利用すると強制終了を避けられる。try 文は、例外処理やクリーンアップ処理を行う際に利用する。
try:
例外が発生する可能性のある処理
except:
補足したい例外が発生したときに実行される処理
else:
例外が発生しなかったときのみ実行
finally:
例外の発生有無にかかわらず実行
raise:
意図的に例外を発生させる
except 節
except 節は、例外が検知されたときに実行される。しかし、引数なしの except を指定すると、あらゆる例外型がキャッチされる。
次に示すコードでは、try ブロックで例外が検知され、except ブロックに記述してあるコードが実行されている。
w = [10, 20, 30]
number = 4
try:
print(w[number])
except:
print(f'{len(w)} より小さい数でお願いします')
else:
print('例外は発生しませんでした')
finally:
print('お疲れ様でした')
# 3 より小さい数でお願いします
# お疲れ様でした
except 節の後ろには、例外型を指定することができる。次のコードは、シーケンスに無効な位置を指定されたときに返される IndexError を指定している。
def n(index_number):
lang = ['Python', 'Rust', 'Go']
try:
return lang[index_number]
except IndexError:
return '無効な位置です'
n(5)
# '無効な位置です'
例外型を複数追加したいときは、except 節の後に半角スペースを空け、括弧とカンマ区切りで複数の例外を記述する。(,)
のようにカンマ区切りで列挙する。
w = [10, 20, 30]
number = 5
try:
print(w[number])
except (IndexError, TypeError):
print('IndexError か TypeError です')
# IndexError か TypeError です
処理を分ける
except 節は複数記述することができる。処理を分けたい場合は、except 節を 任意の数だけ追加するとよい。
w = [10, 20, 30]
number = 'hoge'
try:
print(w[number])
except IndexError:
print('IndexError です')
except TypeError:
print('TypeError です')
# TypeError です
例外型の順番
先に示したように例外クラスは継承の順番があるので、スーパークラスを先に記述すると、サブクラスの例外も補足されてしまう。したがって、継承関係がある例外を複数記述する場合は、サブクラスの例外から記述する。
def num(a, b):
try:
i = a / b
print(i)
except TypeError:
print('TypeError')
except ZeroDivisionError:
print('ZeroDivisionError')
except Exception:
print('Exception')
num(8, 'hoge')
# TypeError
as キーワード
例外の詳細情報を獲得したい場合、except 節の後ろに、as キーワードと変数名をつけ、except ブロック内に埋め込むと、例外オブジェクトの内容を表示することができる。
except (例外型の指定) as 変数名
次のコードは、除数が 0 になり計算できないので、ZeroDivisionError が発生し、それを捕獲している。
def num(a, b):
try:
i = a / b
print(i)
except ZeroDivisionError as er:
print(f'{er}')
num(8, 0)
# division by zero
次に示すコードでは、シーケンスに無効な位置を指定されたときに返される IndexError を捕獲し、メッセージが表示される。
w = [10, 20, 30]
number = 5
try:
print(w[number])
except (IndexError, TypeError) as er:
print(f'{er}')
# list index out of range
else 節 と finally 節
else 節は、例外が発生しなかったときに実行される。正常終了時のみの実行である。
finally 節は、例外の有無に関わらず必ず実行される。
try ブロックで例外が検知されない場合は、except ブロックは実行されず、try と else と finally が実行される。
w = [10, 20, 30]
number = 2
try:
print(w[number])
except:
print(f'{len(w)} より小さい数でお願いします')
else:
print('例外は発生しませんでした')
finally:
print('お疲れ様でした')
# 30
# 例外は発生しませんでした
# お疲れ様でした
raise 文
raise 文で例外を発生させることができる。raise で指定できる例外は、Exception を継承した例外クラスと例外オブジェクトである。
raise 例外クラス(メッセージ)
raise NameError('hoge')
# NameError: hoge
try:
raise TypeError('不正な型です')
except TypeError as er:
print(er)
# 不正な型です
例外を再送出する
例外が発生した際、処理を行った後に、再び例外処理を実行したい場合は、例外を再送出させる。
def num(a, b):
try:
i = a / b
print(i)
except Exception as er:
print('例外発生')
raise er
num(8, 0)
# 例外発生
# ZeroDivisionError: division by zero