プログラムにおいて、一つの処理ブロック内で発生しうるエラー(例外)の種類は一つとは限りません。例えば、データの読み込み処理では「ファイルが見つからない」「データ形式が不正」「必要な値が欠けている」など、様々な問題が発生する可能性があります。
Pythonの try-except 文では、複数の except ブロックを記述することで、エラーの種類に応じた適切な対処(エラーハンドリング)を個別に実装できます。
この記事では、複数の例外を捕捉するための構文と、記述する際の重要なルールについて解説します。
複数の例外を捕捉する基本構文
try ブロックの後に、捕捉したい例外の数だけ except ブロックを列挙します。
構文:
try:
# エラーが発生する可能性のある処理
except 例外A:
# 例外Aが発生した場合の処理
except 例外B:
# 例外Bが発生した場合の処理
except Exception:
# 上記以外の全ての例外が発生した場合の処理
プログラムは上から順に except 節を評価し、発生した例外と一致(または継承関係にある)型を見つけた時点で、そのブロックを実行します。実行される except ブロックは一つだけです。
サンプルコード:サーバー設定の検証
具体的な例として、サーバーの設定情報(辞書データ)を読み込み、1プロセスあたりのメモリ割り当て量を計算する関数を作成します。この処理では、以下のエラーが想定されます。
- 設定項目(キー)が存在しない (
KeyError) - プロセス数が0で、割り算ができない (
ZeroDivisionError) - 設定値が数値ではない (
TypeError)
def calculate_memory_per_process(server_config):
print("--- 設定の検証と計算を開始 ---")
try:
# 辞書から値を取得(キーがないと KeyError)
total_mem = server_config["total_memory_gb"]
proc_count = server_config["process_count"]
# 割り算を実行(proc_countが0だと ZeroDivisionError)
# 値が文字列などの場合、割り算で TypeError
memory_allocation = total_mem / proc_count
print(f"成功: 1プロセスあたり {memory_allocation:.2f} GB を割り当てます。")
except KeyError as e:
print(f"設定エラー: 必要なキー {e} が見つかりません。")
except ZeroDivisionError:
print("数値エラー: プロセス数に 0 は指定できません。")
except TypeError:
print("型エラー: メモリ容量とプロセス数は数値で指定してください。")
except Exception as e:
# 想定外のエラーをキャッチする
print(f"予期せぬエラーが発生しました: {e}")
print("--- 処理終了 ---\n")
# --- 実行テスト ---
# 1. 正常なデータ
valid_data = {"total_memory_gb": 32, "process_count": 4}
calculate_memory_per_process(valid_data)
# 2. キー不足 (KeyError)
missing_key_data = {"total_memory_gb": 32}
calculate_memory_per_process(missing_key_data)
# 3. ゼロ除算 (ZeroDivisionError)
zero_proc_data = {"total_memory_gb": 32, "process_count": 0}
calculate_memory_per_process(zero_proc_data)
# 4. 型不正 (TypeError)
invalid_type_data = {"total_memory_gb": "32GB", "process_count": 4}
calculate_memory_per_process(invalid_type_data)
実行結果:
--- 設定の検証と計算を開始 ---
成功: 1プロセスあたり 8.00 GB を割り当てます。
--- 処理終了 ---
--- 設定の検証と計算を開始 ---
設定エラー: 必要なキー 'process_count' が見つかりません。
--- 処理終了 ---
--- 設定の検証と計算を開始 ---
数値エラー: プロセス数に 0 は指定できません。
--- 処理終了 ---
--- 設定の検証と計算を開始 ---
型エラー: メモリ容量とプロセス数は数値で指定してください。
--- 処理終了 ---
このように、発生したエラーの種類に応じて、適切なエラーメッセージを表示し分けることができます。
例外の記述順序と親クラス
複数の except を記述する際、例外クラスの継承関係に注意が必要です。
Pythonの例外はクラス階層を持っており、Exception はほぼすべての例外の親クラスです。そのため、except Exception: を最初に書いてしまうと、すべてのエラーがそこで捕捉されてしまい、後続の個別の except ブロック(KeyError など)に到達しなくなります。
悪い例:
try:
# 処理
except Exception:
# 全てここで止まってしまう
print("エラー")
except KeyError:
# ここには絶対に到達しない(到達不能コード)
print("キーエラー")
ルール: 具体的で細かい例外(子クラス)を先に記述し、包括的な例外(親クラス、Exceptionなど)は最後に記述します。
複数の例外をまとめて処理する
異なる種類の例外に対して、まったく同じ処理を行いたい場合は、例外クラスをタプルとしてまとめて指定できます。
except (ValueError, TypeError) as e:
print(f"値または型に問題があります: {e}")
これにより、コードの重複を防ぎ、簡潔に記述できます。
まとめ
try-except構文では、exceptブロックを複数記述して、エラーの種類ごとに処理を分岐できます。KeyErrorやZeroDivisionErrorなど、想定される具体的なエラーを個別にハンドリングすることで、堅牢なプログラムになります。exceptの順序は重要です。具体的な例外を先に、Exceptionのような包括的な例外は最後に記述します。
