概要
例外処理は単なるエラー回避ではなく、制御構造の一部として機能すべきである。
Pythonは try/except/else/finally
という柔軟な構文によって、
明快で堅牢な例外設計を可能にする。
本稿では、それぞれのブロックが果たす責務と、
実務におけるベストパターンを、読みやすさ・安全性・保守性の観点から解説する。
1. try/except の基本構造
try:
risky_operation()
except ValueError:
handle_value_error()
-
try
: 成功すればそのまま進行 -
except
: 指定例外が発生すれば補足・処理
→ 基本構文だが、ここに else
と finally
を加えることで、より洗練された制御が可能になる
2. else:成功時の明示的分岐
try:
result = divide(x, y)
except ZeroDivisionError:
result = 0
else:
log_success()
-
else
は 例外が発生しなかった場合のみ実行 -
try
ブロックには 「例外を起こす可能性のある処理」だけを書くのが理想 -
else
は 正常系のロジックを外に分離し、可読性を高める
3. finally:リソース解放・終了処理の保証
try:
f = open("file.txt")
data = f.read()
except FileNotFoundError:
handle_missing()
else:
process(data)
finally:
f.close()
-
finally
は 例外の有無に関わらず、必ず実行される - リソース管理やログ出力、状態リセットなどに使用
4. 組み合わせのベストパターン
✅ 例外処理+成功処理+クリーンアップ
try:
conn = connect_db()
data = conn.query("SELECT * FROM users")
except DBError as e:
log_error(e)
else:
handle_data(data)
finally:
conn.close()
→ 明確に「失敗時」「成功時」「後処理」が分離されており、構造が可読
5. 複数の例外を扱う方法
try:
do_work()
except (IOError, ValueError) as e:
handle_error(e)
→ タプルで複数種類の例外をまとめて処理可能
6. 例外の再送出(re-raise)
try:
risky_task()
except Exception as e:
log_exception(e)
raise # 再送出
→ ログや通知だけを処理し、例外自体は伝播させたい場合に使う
7. ユーザー定義例外で明示的な意図を伝える
class InvalidInput(Exception):
pass
def parse(data):
if not data:
raise InvalidInput("Empty data is not allowed")
→ 独自の例外クラスは、ドメイン固有の失敗を明示するための設計要素
8. よくある誤用と対策
❌ すべての例外を握りつぶす
try:
dangerous()
except:
pass # NG: エラー原因が闇に葬られる
→ ✅ 明示的な例外型を指定する
→ ✅ 必ずログを残す
❌ tryブロックが肥大化
try:
prepare()
execute()
finalize()
except SomeError:
handle()
→ ✅ try内には 「例外を起こす箇所」だけを限定的に書く
❌ finallyで例外を再発生させてしまう
finally:
cleanup() # ここで例外が起きると元の例外が消える
→ ✅ try
/except
を finally
の中でも使うべき
結語
Pythonにおける例外構文は、単なるエラー対処ではない。
それは、コードの意図・成功・失敗・後始末を、構造として明示するための言語設計である。
-
try
:リスクを明示する -
except
:異常を把握し回復する -
else
:成功時の後続を分離する -
finally
:安全な終了処理を保証する
Pythonicとは、“失敗を予防するのではなく、優雅に扱うこと”であり、
例外設計はその哲学を構文で表現する最たる場である。