プログラムは、ファイルが無い・数値に変換できない・ゼロで除算する、といった理由で実行中にエラー(例外)が発生します。何もしないとプログラムはそこで止まってしまいます。これに備えて、エラーが起きても処理を続けたり、適切に対応したりする仕組みが例外処理です。Pythonではtryとexceptで書きます。
注意したいのは、except:だけで「すべての例外」を捕まえてしまう書き方です。これは本来気づくべきバグまで握りつぶしてしまうため、避けるべきです。捕まえる例外の種類を指定するのが基本になります。この記事では、実機のPythonで確認しながら、例外処理の使い方を整理します。
try:に通常処理、except エラー型:にエラー時の処理を書きます。- 捕まえる例外は種類を指定します(
except ValueError:など)。複数の型はexcept (A, B):とまとめられます。 - 例外の中身は
except エラー型 as e:で受け取り、str(e)でメッセージが得られます。 else:は例外が起きなかったとき、finally:は成功・失敗にかかわらず必ず実行されます。- 自分で例外を起こすには
raise ValueError("メッセージ")を使います。 except:だけの「全部捕まえ」は避け、必要な例外だけを捕まえます。
例外処理は関数やファイル操作と一緒に使うことが多いので、関数(def)の使い方、データ構造のリスト(list)や辞書(dict)もあわせて参考になります。
try/exceptの基本
エラーが起きうる処理をtry:に書き、エラーが起きたときの処理をexcept:に書きます。tryの中でエラーが発生すると、すぐにexceptへ移ります。
# 文字列を数値に変換(失敗するとエラー)
try:
n = int("abc")
print(n)
except ValueError:
print("数値に変換できませんでした")
# エラーが起きてもプログラムは止まらず、次に進む
print("処理を続行")
実機でも、int("abc")で発生するValueErrorをexcept ValueError:で捕まえられました。例外を捕まえると、プログラムは止まらずにexceptの処理を行い、その後の処理へ進みます。exceptには、捕まえたい例外の種類(ValueErrorなど)を指定するのが基本です。
例外の種類を指定する(複数except)
エラーの種類によって対応を変えたいときは、exceptを複数並べます。「数値変換の失敗」と「ゼロ除算」を別々に処理する、といった使い方です。
def divide(s):
try:
return 10 / int(s)
except ValueError:
return "数値ではありません"
except ZeroDivisionError:
return "ゼロで割れません"
divide("abc") # 数値ではありません
divide("0") # ゼロで割れません
divide("2") # 5.0
# 複数の型を1つのブロックで扱うならタプルでまとめる
try:
risky_operation()
except (ValueError, TypeError):
print("入力が不正です")
実機でも、divide("abc")はValueError、divide("0")はZeroDivisionErrorと、種類ごとに別の処理になりました。同じ対応でよい複数の例外は、except (ValueError, TypeError):のようにタプルでまとめられます。
例外オブジェクトを受け取る(as e)
発生した例外の詳細(メッセージや種類)を知りたいときは、except エラー型 as e:と書きます。eに例外オブジェクトが入り、メッセージや型名を取り出せます。
try:
[1, 2, 3][10]
except IndexError as e:
print(str(e)) # list index out of range
print(type(e).__name__) # IndexError
# ログやエラーメッセージの表示に使う
try:
int("abc")
except ValueError as e:
print(f"変換に失敗しました: {e}")
実機でも、except IndexError as e:でstr(e)がlist index out of range、type(e).__name__がIndexErrorになりました。エラーメッセージをログに残したり、利用者に表示したりするときに役立ちます。
else と finally
tryには、elseとfinallyも付けられます。elseは例外が起きなかったときに実行され、finallyは成功・失敗にかかわらず必ず実行されます。
def safe_divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("ゼロ除算エラー")
else:
# 例外が起きなかったときだけ実行
print(f"計算成功: {result}")
finally:
# 必ず実行(後始末に使う)
print("処理を終了します")
safe_divide(10, 2)
# 計算成功: 5.0
# 処理を終了します
safe_divide(10, 0)
# ゼロ除算エラー
# 処理を終了します
実機で確認したところ、finallyは例外が起きても起きなくても必ず実行され、さらにtryの中でreturnした場合でも実行されました。ファイルや接続のクローズなど、「何があっても必ず行いたい後始末」をfinallyに書きます。elseは「例外が起きなかったとき」だけ実行されるので、「tryが成功したときの続きの処理」を書くのに向いています。
raiseで例外を発生させる
入力チェックなどで、自分から例外を起こして処理を止めたいときはraiseを使います。raise エラー型("メッセージ")と書きます。
def check_age(age):
if age < 0:
raise ValueError(f"年齢が不正です: {age}")
return age
try:
check_age(-5)
except ValueError as e:
print(e) # 年齢が不正です: -5
# except の中で raise すると、例外を上位へ投げ直せる(再送)
try:
try:
int("abc")
except ValueError:
print("ログを記録")
raise # そのまま上位へ投げ直す
except ValueError:
print("上位で最終処理")
実機でも、raise ValueError("年齢が不正です: -5")で発生させた例外をexceptで捕まえられました。exceptの中でraise(引数なし)と書くと、今捕まえた例外をそのまま上位へ投げ直す(再送する)ことができます。「ログだけ記録して、エラー自体は上位に任せる」といった場面で使います。
【重要】広すぎるexceptは避ける
やってしまいがちなのが、except:だけ、またはexcept Exception:ですべての例外を捕まえる書き方です。一見、安全に見えますが、本来気づくべきバグ(変数名の打ち間違いなど)まで握りつぶしてしまい、原因が分からなくなります。
# NG: すべてを捕まえて握りつぶす(バグも隠れる)
try:
result = some_function()
except:
pass # 何のエラーか分からず、バグも気づけない
# NG: 範囲が広すぎる
try:
process()
except Exception:
print("エラー") # どんなエラーでも同じ扱い
# OK: 捕まえる例外を具体的に指定する
try:
value = int(user_input)
except ValueError:
print("数値を入力してください")
except:だけの書き方は、KeyboardInterrupt(Ctrl+Cでの中断)など、本来止めるべき割り込みまで捕まえてしまいます。また、想定外のバグも握りつぶすため、不具合の発見が遅れます。「どの例外を、なぜ捕まえるのか」を明確にして、種類を具体的に指定してください。どうしても広く捕まえる必要があるなら、せめてexcept Exception as e:としてeを記録(ログ出力)し、何が起きたか分かるようにします。なお、複数のexceptを書くときは、具体的な型を上、汎用のExceptionを最後に並べます(順番が逆だと、先にExceptionで捕まり、具体的なexceptに届きません)。
よくある例外の種類
Pythonには多くの組み込み例外があります。よく出会うものを知っておくと、exceptで正しく指定できます。
| 例外 | 起きる場面 |
|---|---|
ValueError |
値が不適切(int("abc")など) |
TypeError |
型が合わない(数値と文字列の足し算など) |
KeyError |
辞書に存在しないキーを指定 |
IndexError |
リストの範囲外のインデックス |
FileNotFoundError |
ファイルが見つからない |
ZeroDivisionError |
ゼロで割った |
AttributeError |
存在しない属性・メソッドを呼んだ |
どの例外が起きるか分からないときは、まずexcept Exception as e:でtype(e).__name__を表示させて型を確認し、それを具体的なexceptに指定する、という進め方が確実です。
よくある失敗
except: だけですべて握りつぶす
本来気づくべきバグまで隠れます。捕まえる例外の種類を具体的に指定します。
具体的な例外より先にExceptionを書く
except Exception:を先に書くと、そこですべて捕まり、後ろの具体的なexceptに届きません。具体的な型を上、汎用を最後に並べます。
finallyで後始末を書き忘れる
ファイルや接続は、エラーが起きても閉じる必要があります。finally(またはwith文)で確実に後始末します。
例外を握りつぶしてログも残さない
except: passでは原因が分かりません。最低でもexcept Exception as e:でeをログに残します。
try の範囲を広げすぎる
tryに多くの処理を入れると、どこでエラーが起きたか分かりにくくなります。エラーが起きうる箇所に絞って囲みます。
よくある質問
except ValueError:など)。except:だけだとすべての例外を捕まえ、バグも隠してしまうため避けます。どの例外か分からないときは、まずexcept Exception as e:でtype(e).__name__を表示して型を調べます。elseは例外が起きなかったときだけ実行され、try成功時の続きの処理に使います。finallyは成功・失敗にかかわらず必ず実行され、ファイルのクローズなどの後始末に使います。try内でreturnしてもfinallyは実行されます。except (ValueError, TypeError):のように、型をタプルでまとめます。別々の処理にしたいときは、exceptを複数並べ、具体的な型を上、汎用のExceptionを最後に書きます。raise ValueError("メッセージ")を使います。入力チェックで不正な値を見つけたときなどに、処理を止めて呼び出し元に知らせます。exceptの中で引数なしのraiseを書くと、捕まえた例外を上位へ投げ直せます。except エラー型 as e:と書き、str(e)でメッセージ、type(e).__name__で例外の型名が得られます。ログ出力や利用者へのエラー表示に使います。まとめ
try:に通常処理、except エラー型:にエラー時の処理を書きます。- 捕まえる例外は種類を具体的に指定します。複数は
except (A, B):でまとめられます。 - 例外の中身は
except エラー型 as e:で受け取り、str(e)で取り出します。 elseは成功時、finallyは必ず実行され、後始末に使います。- 自分で起こすなら
raise。except内のraiseで再送できます。 except:だけの「全部捕まえ」は避け、具体的な型を上・汎用を最後に並べます。
例外処理は、プログラムを止めずにエラーへ対応するための仕組みです。「捕まえる例外を具体的に指定する」「後始末はfinally」という2点を押さえれば、堅牢で原因の分かりやすいコードを書けるようになります。

