プログラムを実行するうえでエラーはつきものなので、エラーが発生した場合にそれををどのように上位層へ通知するかを考慮しなければなりません。
プログラムを書いているなかで、ある処理を関数に切り出した際にエラー通知をどのようにするのが適切か迷うことがあったため、実装したプログラムが別のプログラムから呼び出されるという観点から、どの方法が適切なのかをまとめてみました。
なお、タイトルで「Pythonのプログラム」としていますが、他の言語にもある程度適用できる考え方かと思います。
エラー通知の方法
代表的なエラー通知の方法は次の2つがあるかと思います。
- 戻り値による通知
- 例外による通知
戻り値で通知
Cでよく使われるようなint型の戻り値でエラーを通知する方法。
def sample_1(a, b):
:
if a > b:
return -1
:
戻り値にエラー有無と処理結果をタプルで返す方法。
def sample_2(a, b):
:
if a > b:
return False, None
:
あるいはNoneを返すことでエラーを表すケースもあると思います。
def sample_3(a, b):
:
if a > b:
return None
:
例外で通知
戻り値を使わずにエラー通知を例外に任せる方法です。
通常のシステム例外、あるいは意図的に例外を送出することで呼び出し元にエラー通知をすることができます。
意図的に例外を送出する場合、Pythonでは raise
文を使って次のように書きます。
def sample_4():
:
if a > b:
raise Exception('Error!')
:
呼び出し側でのエラーの受け取り方
戻り値でエラーを通知する場合、呼び出し元のプログラムでエラーチェックを適切に行う必要があります。
上記のsample_3.pyを例にすると、エラーの際に None
を返す関数を呼び出した場合、次のようにエラーチェックの実装をします。
このエラーチェックを行わない場合、sample_3
の処理中に何らかのエラーが発生したとしてもプログラム自体はエラーにならず、そのまま処理が継続されます。
result = sample_3()
if result is None:
print('Error!')
sys.exit(1)
反対に例外でエラーを通知する場合は、上記のようなエラーチェックを明示的に行わなくてもエラーが発生した時点で例外が送出されてプログラムが停止します。
処理を継続したい場合はtry文を使って例外をキャッチすることができます。
try:
result = sample_3()
except Exception:
<何らかの処理>
呼び出し側の目線で考えると、それぞれ次のような特徴があります。
- 戻り値による通知
- エラーチェックを明示的に行う必要がある
- エラーチェックを行わないことでプログラムが継続する
- 例外による通知
- 明示的なエラーチェックは必要ない
- 例外をキャッチすることでプログラムが継続する
どちらの方法が適切なのか
結論としては、Pythonにおいては基本的に例外を使うのがよいと思います。
理由は、例外による通知は暗黙的にエラーが無視されることがないためです。
例外が発生すると通常プログラムはその時点で停止します。エラーが発生した異常状態のプログラムがそのまま動き続けると、予期しない他の重大なエラーを発生させる可能性があります。
そのため、異常状態になったプログラムは一刻も早く停止させるべきであり、それを考慮するとエラーが発生した時点で停止するプログラムはエラーによる二次障害のリスクを抑えた設計であるといえます。
エラーが発生した後もプログラムを継続したい場合はtry文を使って例外をキャッチして処理を継続させることができますが、重要なのはそのために例外処理のコードを明示的に書かなければいけない点です。
戻り値を使ったエラー通知の場合、呼び出し元で明示的にエラーチェックの処理を実装しなければならず、その実装を省くことで簡単にエラーを無視できていしまいますし、例外を使った場合とは反対にその状況というのは無意識のうちに発生しうるものです。
上記のような理由から、Pythonによるプログラムのエラー通知としては例外を利用するのがよいと考えます。
まとめ
Pythonプログラムにおけるエラー通知の適切な方法について、安全性を重点においた考え方で自分なりに解を出してみました。
間違いなどありましたらコメントいただけると幸いです。