##例外とは?
コードに間違いがあり、プログラムをコンパイルした際にエラーが発生することを「コンパイルエラー」と言いますが、
コンパイルは正常に終了しても、その後実行中に何らかの異常が発生することを例外と言います。
自分の記事の中ではこれまで意味が伝わるように「エラー」という言葉で「例外」を表現していましたが、
今後は正しい単語である「例外」を使用していきます。
これまでの記事で紹介した例外は以下のようなものがありました。
- 数値型と文字列型を+で結合する
- リスト.remove(要素)でリスト内に存在しない要素を削除しようとする
- ディクショナリでget(キー)を用いずに、存在しないキーを指定する
他にも有名な例外を発生させるコードが、数値を0で割るゼロ除算です。
a = 10 / 0
print("{0}".format(a))
上記プログラムはコンパイル時にはエラーを発生させませんが、実行時に以下のように例外を発生させます。
ZeroDivisionError: integer division or modulo by zero
例外を発生しうるコードの箇所に「もし例外が発生したら~」と記述することを例外処理と言います。
例外の捕捉
プログラムが実行中に突然終了してしまっては困ります。
そこで、上に書いたゼロ除算のプログラムを、実行時に例外を発生させないよう修正します。
try:
a = 10 / 0
print("{0}".format(a))
except ZeroDivisionError:
print("ZeroDivisionError!!")
例外を発生しうるコードを**try:で括ります。そして例外が発生した場合の処理をexcept 例外の種類 :**で括ります。
「例外の種類」は適切なものを記述しなければなりません。
例えば上記のプログラムでZeroDivisionError(=ゼロ除算例外)ではなく、
以下のようにValueError(=変数の型に合わない値が格納された例外)を指定すると、例外を捕捉しません。
try:
a = 10 / 0
print("{0}".format(a))
except ValueError:
print("ZeroDivisionError!!")
上記のプログラムを実行すると例外が発生します。
例外に関する情報の利用
先ほどのプログラムでは例外が発生した場合、自分で出力するメッセージを作成していましたが、
Pythonでは例外発生時に情報を保持しているため、これを利用できます。
以下のプログラムを実行してみてください。
try:
a = 10 / 0
print("{0}".format(a))
except ZeroDivisionError as e:
print("type:{0}".format(type(e)))
print("args:{0}".format(e.args))
print("message:{0}".format(e.message))
print("{0}".format(e))
exception 例外の種類 as 変数 : と記述すると、エラーの情報を持つ変数を定義できます。
出力結果は以下のようになります。自分で作成したメッセージの代わりにこれらを出力しても良いでしょう。
type:<type 'exceptions.ZeroDivisionError'>
args:('integer division or modulo by zero',)
message:integer division or modulo by zero
integer division or modulo by zero
複数の例外を捕捉する
先ほど書いたように、「例外の種類」は適切なものを記述しなければなりませんが、複数パターンの例外が
発生する場合は、分岐のelif同様に複数書くことが出来ます。
try:
f = open(file_name,'w')
data = dict_input['data']
f.write(data)
f.close()
except KeyError:
print('キーが見つかりませんでした')
except (FileNotFoundError, TypeError) :
print('ファイルが開けませんでした')
except:
print('何らかのエラーが発生')
これはファイルをオープンするプログラムの処理の一部ですが、ファイルの入出力は
『Python基礎講座』では説明しないため、細かいコードの説明は省きます。
見て欲しいのはexcept KeyError、except (FileNotFoundError,TypeError)、except の部分です。
try句の中で例外が発生した場合、Pythonは初めにその例外がKeyErrorで補足出来るかを確認します。
補足できれば「キーが見つかりませんでした」と出力します。
KeyErrorで捕捉出来なかった場合、FileNotFoundErrorまたはTypeErrorで捕捉出来るかを確認します。
補足できれば「ファイルが開けませんでした」と出力します。
このように複数の例外に対して同一の処理を行うことが可能です。
それでも捕捉出来なかった場合、最後のexcept は『全ての例外』を意味します。分岐のelseと同じ意味を持ちます。
ここまで到達した場合は「何らかのエラーが発生」と出力します。
複数の例外を書かずにexcept : だけ用意すれば全ての例外を捕捉してくれますが、
全ての例外を同じexcept句で捕捉してしまうと、例外発生の原因が分かりにくくなるので、
各例外の種類を書いた上で最後にexcept :を記述すると良いでしょう。
else/finally
elseは「try句内で例外が発生せずに最後まで処理が進んだ場合」処理を実行します。
finallyは「例外の発生に関係なく、最後に」処理を実行します。
try:
a = 10 / 0
# a = 10 / 1
print("{0}".format(a))
except ZeroDivisionError as e:
print("ZeroDivisionError!!")
else:
print("else statement")
finally:
print("finally statement")
上記のプログラムを実行してください。その後、a = 10 / 0 をコメントアウトし、下の a = 10 / 1 のコメントを外して
再度実行し、両者の出力が説明のとおりであることを確認してください。
raise
raiseを使用すると、故意に例外を発生させることが出来ます。
raise 引き起こしたい例外(送出したい例外) を例外を発生させたい箇所に記述し、
except句の中でraise と記述することで、例外を返します。
以下のプログラムを実行後、except句内のraiseをコメントアウトして両者の動作の違いを確認してください。
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
raise
例外の説明は以上になります。