例外処理

アプリケーションシステムは、膨大量の関数が適切に制御し合って動作している。関数が多くなると、想定外のことが起こりやすくなる。中でも、1 つの関数が予期しない動作により、システム全体の異常動作を引き起こすこともある。これを防ぐために、個々の関数が予期しない動作になったときの対処方法を予め決めればよい。例えば、ある関数が異常動作したとき、現在までのデータを保存してからシステムを正常終了したり、あるいは時間を置いてからその関数を再実行したりするというような処理を決める。このような、関数が予期しない動作になったときに、実行する処理を例外処理という。

例外処理に関する具体例を取り上げてみる。あるデータを解析するために、そのデータに対して 3 つの処理を行う必要があるとする。このうち、最初の処理は確率 0.9 で失敗するものとする。データ解析を自動化させるためには、処理 1 を確実に成功させる必要がある。しかし、次のようなコードを書くと、ほとんどの場合、analyze1_data 関数でエラーとなって、途中で異常終了する。

import random

def analyze1_data():
    p = random.random()
    if p < 0.9:
        print('success 1')
    else:
        raise ValueError()

def analyze2_data():
    print('success 2')

def analyze3_data():
    print('success 3')


analyze1_data()


analyze2_data()
analyze3_data()

そこで、対処方法として、analyze1_data 関数がエラーとなった場合、もう一度実行すればよい。つまり、例外処理として「関数 analyze1_data がエラーを出した場合、analyze1_data をもう一度実行する」のような制御を書けば良い。これには、エラーが出た時の処理(例外処理)と無限ループを使う。まず analyze1_data 関数を無限回実行する構文を書く。次に、解析に成功できたならばこの無限ループを抜けるように制御し、解析に失敗したならば画面上に「Try again!」と表示させてもう一度無限ループを繰り返すようにする。

import random

def analyze1_data():
    p = random.random()
    if p > 0.9:
        print('success 1')
    else:
        raise ValueError()

def analyze2_data():
    print('success 2')

def analyze3_data():
    print('success 3')


while True:
    try:
        analyze1_data()
        break
    except:
        print('Try again!')


analyze2_data()
analyze3_data()

例外処理は、基本的に例外が発生する可能性のある処理 X を try ブロック内で実行し、例外発生時に行う処理を except ブロックに書く。このほかに、処理 X に例外が発生しなかったときに行う処理、あるいは例外発生の有無を問わずに行う処理も制定することができる。これらの処理は else および finally ブロックに書く。

Python の例外処理は try, except, else, finally で制御する。
try:
    analyze1_data()

except:
    print('Try again!')

else:
    analyze2_data()
    analyze3_data()

finally:
    print('All processes done!')

finally ブロックを付けることによって、例えば以下のように、システムの処理を安全に終わらせることができる。

  • ファイル処理中に例外の発生の有無に関係なく、最後にファイルを安全に閉じる処理。
  • データベースとの通信時に例外の発生の有無に関係なく、データベースからログアウトする処理。

例外は様々な種類がある。例えば、ファイルが見つからないときの例外、メモリ不足したときの例外などがある。このとき、必要に応じて except を増やせば、例外の種類に応じて、例外処理を多数制定できる。一般的によく見られる例外は、「組み込み例外」として定義されている。例えばファイルが見つからないときは FileNotFoundError、メモリが不足したときは MemoryError などのように定義されている。例外をユーザー自身で定義することも可能。

try:
    analyze1_data()
    
except FileNotFoundError:
    print('No file! Check you really have the file!')
    
except MemoryError:
    print('You need buy new machine!')
    
except:
    print('I don\'t know what happened!')