LoginSignup
4

More than 5 years have passed since last update.

Python Error Handling

Posted at

Pythonでのエラー処理について自分なりにメモを取っていたものを公開します。
詳しくは以下のリンクを参照してください。

環境

Python 3.x

Errorとは?

エラーには (少なくとも) 二つのはっきり異なる種類があります。それは 構文エラー (syntax error) と 例外 (exception) です。

構文エラーは、Pythonの文法として間違っているものを指します。コードの外面的なエラーということもできます。Pythonはコードブロックをインデントにより表現するので、可読性が高く構文エラーを引き起こしにくい言語である気がします。

その一方で、例外はコードの内面的なエラーということができると思います。
たとえば、以下のコードはPythonのコード的には正しいものですが、数学的におかしなものですよね。

test = 10 / 0 # <= 0で除算することはできません!

このように、コードを実行したタイミングで起こる不具合が、例外と言えます。

Errorに立ち向かう

構文エラーは、インタープリターがプログラムを解釈する際に自動的に検出してくれるため、怒られた箇所を修正すれば良いです。

しかし、例外にはどのように対処すればよいのでしょうか?

Pythonでは、例外を処理するためにtryexceptといった構文が用意されています。
具体例を見てみましょう。

try:
    answer = 10 / 0
except ZeroDivisionError:
    print("divided by zero!!!")

上記のコードには、ポイントが二つあります。

  1. try節のブロック内で、例外が起こりうるコードを実行する
  2. except節のブロック内で、try節で起こりうる例外のを指定して、その場合の処理を実行する

この場合は、0による除算が行われたことを示す例外であるZeroDivisionErrorがtry節から発生し、それをexcept ZeroDivisionError以下で処理しています。

このように、例外を処理するためにはtry ~ exceptを利用しましょう。

さまざまな利用ケース

関数の実行をサポートする

二つの数値を引数として受け取り、その商を返すdivide関数を以下のように定義します。

def divide(x, y):
    answer = x / y
    return answer

一見良さそうです。しかし、いままでにみてきたように割り算には注意が必要です。
divide(10, 0)などが実行された暁にはZeroDivisionErrorが発生してしまいます。
第2引数として0がわたされる(文字通りの0ではなく計算の結果として0が渡されてしまう)ケースも考慮して、以下のように関数を改良してみましょう。

import math

def divide(x, y):
    try:
        answer = x / y
    except ZeroDivisionError:
        print("numbers cannot be divided by zero!")
        answer = math.nan
    return answer

これで0で割ってしまった場合も、一応はプログラムは動くようになりました!
ただし、引数として文字列タプルなど、想定しないものが渡された場合はどうすれば良いでしょうか...
ここではその答えは記述しませんが、以下のようなポイントを押さえて実装してみてください。

  1. 演算を正しく行えない場合、どのような例外の型が送出されるのか
  2. 例外に対する正しいフォロー(except節での処理)が必要になりそうか

あまり例外ばかりを気にしてしまうと、コードが膨れ上がって可読性を損なうこともあるので、ケースバイケースで考えましょう。

例外の発生の有無を問わない共通の処理をはさむ

例外が起こるにせよ、怒らないにせよ何らかの処理を共通して挟みたい場合があると思います。
具体例としては少し苦しいですが、以下のコードをみてみてください。

try:
    answer = 10 / 0
except ZeroDivisionError as e:
    answer = math.nan
finally:
    print(answer)

finallyという新しいキーワードが出てきました。
このキーワードを利用することで、例外が起こった場合にも、起こらなかった場合にもfinally節で指定した処理が行われることになります。

ちなみに、except節でas eとしていますが、これは発生した例外オブジェクトをeという変数で保持して、その後のブロック内で参照できるようにしているものです。
そのため、以下のようにコードを変更した場合、例外の内容を出力してくれたりします。

# 省略
except ZeroDivisionError as e:
    print(type(e)) # => <class 'ZeroDivisionError'>
    print(e.args)  # => ('division by zero',)
    print(e)       # => division by zero

変数のスコープはどうなっているの?

Pythonでは、変数のスコープは基本的にコードのインデントにより決定されます。
それでは、以下のコードで定義されているam_i_visibleという変数は、一番したのprint()において参照できるでしょうか?

try:
    am_i_visible = "yes"
except:
    do_something()

print(am_i_visible) # => ???

正解は、参照できます。yesが出力されると思います。
あくまで、try節がどこまでかを明確にするためにインデントを利用しているので、この場合は参照ができます。

Pythonはスコープが少しややこしいときがありますね...
以下のfor文では、変数nがグローバル変数になってしまいますし...

sum = 0
for n in range(1, 5):
    sum = sum + n

print(n) # => 4

最後に

簡単ではありますが、Pythonのエラー処理についてまとめてみました。
Pythonの例外処理には、まだまだ触れるべきことがありますが、一旦ここでおわります。

自分自身まだまだPython歴が浅いので、アドバイスなどありましたらぜひコメント欄に書き込んでください。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4