Python、例外処理の落とし穴
例外処理が何か知らない方は👇を事前に読む。
Pythonの例外処理(try, except, else, finally)
例外処理は便利だ。何がって?どんなに深くネストした関数の底からでも、上位層の海面まで引っ張り上げてくれる事だ。処理を一つ一つこなしてゆき、どこかで問題が見つかれば総合的にデータの廃棄処理を実行する。これはプログラムを簡潔にするのに役立つ、そして”止まらないプログラム”を書くことが比較的容易になる。
ところが、便利と落とし穴は表裏一体といえる。プログラムは書いたとおりにしか動かないと言うが、自分の意図したとおりに動くとは限らない。この書いた≒意図したが成立してしまう原因が思い込みである。これを避けるには実際にコードを書いて仕様と意図の差を埋める作業が必要になる。
def divide(i):
try:
print(1)
return 5 / i + int(i)
except ZeroDivisionError as e:
print(2, e)
except Exception as e:
print(3, e)
else:
print(4, "success")
finally:
print(5, "finaly")
この関数にdivide(0), divide(1), divide("2")を入れたときに出力されるデータを正確に予想できる人はいるかな?
(/ω\)
p|д゚)
答え
>> divide(0)
1
2 division by zero
5 finaly
>> divide(1)
1
5 finaly
Out[13]: 6.0
>> divide("2")
1
3 unsupported operand type(s) for /: 'int' and 'str'
5 finaly
ここで重要なのはfinallyを必ず通る事である。一般的にreturn文は強力なので、慣れている人だとdivide(1)とdivide("2")はfinallyを出力しないと考える事が多い。
この思い込みを埋めないとバグの元になる。しかも、書いた本人は正しく理解している前提で書いているのでデバッグが困難になる。
そもそもバグの存在が認識できれば良い方で、穴を知らないままリリースし誰かの手元で爆発するまで問題にならない事も多い。さらに問題が起こるのが”時々ふとした瞬間に”だとメガトン級だろう。