Help us understand the problem. What is going on with this article?

Python基礎講座(11 例外)

More than 5 years have passed since last update.

例外とは?

コードに間違いがあり、プログラムをコンパイルした際にエラーが発生することを「コンパイルエラー」と言いますが、
コンパイルは正常に終了しても、その後実行中に何らかの異常が発生することを例外と言います。
自分の記事の中ではこれまで意味が伝わるように「エラー」という言葉で「例外」を表現していましたが、
今後は正しい単語である「例外」を使用していきます。

これまでの記事で紹介した例外は以下のようなものがありました。

  • 数値型と文字列型を+で結合する
  • リスト.remove(要素)でリスト内に存在しない要素を削除しようとする
  • ディクショナリでget(キー)を用いずに、存在しないキーを指定する

他にも有名な例外を発生させるコードが、数値を0で割るゼロ除算です。

division_by_zero.py
a = 10 / 0
print("{0}".format(a))

上記プログラムはコンパイル時にはエラーを発生させませんが、実行時に以下のように例外を発生させます。

ZeroDivisionError: integer division or modulo by zero

例外を発生しうるコードの箇所に「もし例外が発生したら~」と記述することを例外処理と言います。

例外の捕捉

プログラムが実行中に突然終了してしまっては困ります。
そこで、上に書いたゼロ除算のプログラムを、実行時に例外を発生させないよう修正します。

division_by_zero2.py
try:
    a = 10 / 0
    print("{0}".format(a))
except ZeroDivisionError:
    print("ZeroDivisionError!!")

例外を発生しうるコードをtry:で括ります。そして例外が発生した場合の処理をexcept 例外の種類 :で括ります。
「例外の種類」は適切なものを記述しなければなりません。
例えば上記のプログラムでZeroDivisionError(=ゼロ除算例外)ではなく、
以下のようにValueError(=変数の型に合わない値が格納された例外)を指定すると、例外を捕捉しません。

division_by_zero_EXCEPTION2.py
try:
    a = 10 / 0
    print("{0}".format(a))
except ValueError:
    print("ZeroDivisionError!!")

上記のプログラムを実行すると例外が発生します。

例外に関する情報の利用

先ほどのプログラムでは例外が発生した場合、自分で出力するメッセージを作成していましたが、
Pythonでは例外発生時に情報を保持しているため、これを利用できます。
以下のプログラムを実行してみてください。

py3.division_by_zero_info.py
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 KeyErrorexcept (FileNotFoundError,TypeError)except の部分です。
try句の中で例外が発生した場合、Pythonは初めにその例外がKeyErrorで補足出来るかを確認します。
補足できれば「キーが見つかりませんでした」と出力します。
KeyErrorで捕捉出来なかった場合、FileNotFoundErrorまたはTypeErrorで捕捉出来るかを確認します。
補足できれば「ファイルが開けませんでした」と出力します。
このように複数の例外に対して同一の処理を行うことが可能です。
それでも捕捉出来なかった場合、最後のexcept は『全ての例外』を意味します。分岐のelseと同じ意味を持ちます。
ここまで到達した場合は「何らかのエラーが発生」と出力します。

複数の例外を書かずにexcept : だけ用意すれば全ての例外を捕捉してくれますが、
全ての例外を同じexcept句で捕捉してしまうと、例外発生の原因が分かりにくくなるので、
各例外の種類を書いた上で最後にexcept :を記述すると良いでしょう。

else/finally

elseは「try句内で例外が発生せずに最後まで処理が進んだ場合」処理を実行します。
finallyは「例外の発生に関係なく、最後に」処理を実行します。

try_else_finally.py
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をコメントアウトして両者の動作の違いを確認してください。

raise.py
try:
    raise NameError('HiThere')
except NameError:
    print('An exception flew by!')
    raise

例外の説明は以上になります。

次: Python基礎講座(12 関数)

bltinc
広告・人材・D2Cと幅広い事業を展開するベンチャー企業 ・Blog : https://blog.bltinc.co.jp/
https://bltinc.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away