一行の入力ミスで全体が止まると、残りのどこまで処理できたか分からなくなります。ただ、exceptで何でも無視すると、失敗した分まで成功したように見えます。続けられる不備と、結果を確定する前に止める条件を分けます。
exceptを置く範囲
tryの中で例外が起きると、その後の処理は飛ばされ、種類が合うexceptへ移ります。ファイルを開く、数値へ変換する、結果を保存する処理を全部まとめて囲むと、どの失敗を想定したのか読み取りにくくなります。
入力値を数へ変えるときのValueErrorと、ファイルがないときのFileNotFoundErrorは、確認先が違います。原因を区別したい場所ごとに、受け取る例外を決めます。
不備を記録して最後に判断する例
次は架空の金額三件を調べる例です。すべての入力を確認しますが、不備があれば合計を完成結果として出さず、終了コード1で止めます。
raw_values = ["1200", "-5", ""]
amounts = []
errors = []
for row_number, raw in enumerate(raw_values, start=1):
try:
amount = int(raw)
if amount < 0:
raise ValueError("負数は対象外です")
except ValueError:
errors.append(row_number)
else:
amounts.append(amount)
print(f"有効{len(amounts)}件・要確認{len(errors)}件")
if errors:
print("要確認の行:", errors)
raise SystemExit(1)
print("合計:", sum(amounts))
この入力では有効1件・要確認2件となり、2行目と3行目を確認するために停止します。負数を対象外にするのは、この例の入力ルールです。返品を負数で扱う業務なら、その条件を変更します。
入力ミスを0として足す処理にはしていません。途中まで計算できても、確認が必要な行を含んだまま報告を確定しない、という分け方です。
elseとfinallyの役割
elseは、tryの中で例外が起きなかった場合に実行します。上の例では変換と検査に通った値だけを結果へ加える場所です。
finallyは例外の有無にかかわらず後片付けを行うために使います。ファイルならwithで管理できる場合もあります。finallyの中で新しい例外を出したり、returnしてしまったりすると、元の失敗が見えにくくなるので、必要な後片付けへ絞ります。
分からない失敗を隠さない
想定していない例外は、記録したうえでraiseし直す方法があります。エラーが起きたという事実を、呼び出した側へ伝えるためです。ログへ書いただけで成功の終了コードを返すと、定期実行側が正常と判断することがあります。
通知や投稿の処理では、応答が返らなくても相手側で完了している場合があります。そのような結果不明を、例外が出たから未実行と決めて再送しないことも必要です。例外処理の形だけでなく、どの操作を再実行してよいかまで、処理の内容に合わせて決めてください。
関連する作業
失敗の記録と、入力データを先に点検する方法は次の記事で確認できます。
