例外処理
プログラムの実行中には予期しない状況が常に発生します——ファイルが存在しない、ネットワークが切断される、ユーザーが無効なデータを入力する……これらの「例外」を処理しないと、プログラムはクラッシュします。例外処理により、プログラムがエラーから回復し、不可解なエラーメッセージの代わりにユーザーフレンドリーなプロンプトを表示できるようになります。
1. 例外とは
例外とは、プログラムの実行中に発生するエラーです。例外が処理されないと、プログラムは終了します:
# このコードはクラッシュする
# print(10 / 0) # ZeroDivisionError: division by zero
# int("abc") # ValueError: invalid literal for int()
# open("non_existent.txt") # FileNotFoundError
try-except を使ってこれらのエラーを適切に処理します:
例:try-except での除算と入力エラー処理
try:
number = int(input("Enter a number: "))
result = 10 / number
print(f"10 / {number} = {result}")
except ValueError:
print("That's not a valid number!")
except ZeroDivisionError:
print("Cannot divide by zero!")
実行例:
Enter a number: abc
That's not a valid number!
try を「考えられるすべてのエラーを予測する」ために使うのではなく、「まず実行し、問題が発生したら対処する」ために使います。Python コミュニティではこれを 「許しを請うことは、許可を得ることより簡単だ」(EAFP) と呼びます。
2. 完全な try-except 構造
例:try-except-else-finally 完全構造
try:
# エラーが発生する可能性のあるコード
file = open("data.txt", "r", encoding="utf-8")
content = file.read()
number = int(content)
print(f"The number is: {number}")
except FileNotFoundError:
print("File not found!")
except ValueError:
print("File content is not a valid number!")
else:
# 例外が発生しなかった場合に実行
print("Successfully read and converted!")
finally:
# 常に実行される(例外の有無にかかわらず)
print("Cleanup: closing file")
try:
file.close()
except NameError:
pass
| 部分 | 実行タイミング | 目的 |
|---|---|---|
try |
常に | エラーが発生しそうなコードをここに書く |
except |
指定した例外が発生したとき | エラーを処理 |
else |
例外が発生しなかったとき | 成功後のロジック |
finally |
常に(例外の有無にかかわらず) | リソースのクリーンアップ(ファイルを閉じる/接続を解放) |
else の使用頻度は低く、finally は広く使われます。 with 文(コンテキストマネージャー)がリソース解放を自動処理し、多くの場合 finally を置き換えられます。
3. よくある例外の種類
# ZeroDivisionError — ゼロによる除算
# int("abc") # ValueError — 無効な値
# [1, 2][5] # IndexError — 範囲外のインデックス
# {"a": 1}["b"] # KeyError — 存在しないキー
# open("x.txt") # FileNotFoundError — ファイルが見つからない
# 10 + "hello" # TypeError — 型エラー
# import nonexistent # ModuleNotFoundError — モジュールが見つからない
すべての例外をキャッチする(推奨しない)
例:例外キャッチの正しい方法と間違った方法
try:
# 何らかのコード
pass
except: # すべての例外をキャッチ
print("Something went wrong")
try:
pass
except Exception as e: # 既知の例外をキャッチ
print(f"Error: {e}")
except: は推奨されません —— KeyboardInterrupt(Ctrl+C)や SystemExit などのシステム例外をキャッチしてしまい、正常なプログラム終了を妨げます。代わりに except Exception as e を使用してください。ベストプラクティスは、予想される特定の例外型のみをキャッチすることです。
4. raise:明示的に例外を発生させる
呼び出し元に「何か問題が発生した」ことを伝える必要がある場合があります。raise を使います:
例:raise で例外を発生
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero!")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(f"Error: {e}") # Error: Cannot divide by zero!
パスワード強度チェックの例
例:raise によるパスワード検証
def validate_password(password):
"""パスワードの強度を検証。条件を満たさない場合は例外を発生"""
if len(password) < 6:
raise ValueError("Password must be at least 6 characters")
if not any(c.isdigit() for c in password):
raise ValueError("Password must contain a digit")
if not any(c.isalpha() for c in password):
raise ValueError("Password must contain a letter")
return True
try:
validate_password("123")
except ValueError as e:
print(f"Invalid password: {e}")
raise を使うタイミングは? 関数が「あるべきでない」状況に遭遇したとき——パラメータ値が無効、前提条件を満たしていない、依存関係が利用不可など。例外を発生させて呼び出し元に対処方法を任せ、関数内でエラーを黙って飲み込まないようにします。
5. カスタム例外クラス
より正確なエラー情報を提供するために、独自の例外型を作成できます:
例:銀行引き出しのカスタム例外
class BalanceError(Exception):
"""残高不足例外"""
pass
class AccountLockedError(Exception):
"""アカウントロック例外"""
pass
def withdraw(balance, amount):
if amount > balance:
raise BalanceError(f"Insufficient balance! Need {amount}, have {balance}")
if balance < 0:
raise AccountLockedError("Account is locked")
return balance - amount
try:
withdraw(100, 200)
except BalanceError as e:
print(f"Transaction failed: {e}")
except AccountLockedError as e:
print(f"Transaction failed: {e}")
Exception を継承するだけです。通常、クラス名だけで十分で、追加のメソッドは必要ありません。
6. logging:プロフェッショナルなエラーログ
シンプルなスクリプトでは print() でエラーメッセージを出力しても問題ありませんが、プロダクションプロジェクトでは logging モジュールを使用します:
例:logging の設定と使用
import logging
# 基本設定
logging.basicConfig(
level=logging.INFO, # ログレベル
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("app.log", encoding="utf-8"), # ファイルに書き込み
logging.StreamHandler() # コンソールにも出力
]
)
# 異なるログレベル
logging.debug("Debug info") # デバッグ情報、通常は出力しない
logging.info("Application started successfully") # 一般情報
logging.warning("Disk usage at 85%") # 警告
logging.error("Database connection failed") # エラー
logging.critical("System crash!") # 致命的エラー
出力(ファイルとコンソールの両方に書き込まれる):
2026-06-23 16:00:00,123 [INFO] Application started successfully
2026-06-23 16:00:00,124 [WARNING] Disk usage at 85%
2026-06-23 16:00:00,124 [ERROR] Database connection failed
print() に対する logging の利点: 1)コンソールとファイルに同時に出力可能。2)レベル制御あり(開発時はすべて表示、本番時は WARNING+ のみ)。3)タイムスタンプが自動付加。4)リリース前に大量の print() 文を削除する必要がない。
よくあるユースケース
- ユーザー入力検証:
try-exceptでint()変換エラーを処理し、ユーザーフレンドリーなプロンプトを表示 - ファイル操作:
try-exceptでファイル未発見、権限拒否などのエラーを処理 - ネットワークリクエスト:タイムアウト、接続失敗、データ解析エラーを処理
- データベース操作:接続失敗、SQL 構文エラー、一意制約違反を処理
- API 開発:カスタム例外で様々なエラー種別を正確に区別
❓ よくある質問
except Exception as e の as e は何を意味しますか?as e はキャッチした例外オブジェクトを変数 e に代入し、例外の詳細を調べられるようにします——print(e) でエラーの説明を表示、type(e).__name__ で例外の型名を表示。as e がなくても例外はキャッチできますが、詳細なエラー情報は見られません。if を使います。予測できないエラー(ネットワークリクエスト、ファイル操作など)には try-except を使います。Python コミュニティの EAFP スタイルは try の使用を好みます——しかしこれは美的な問題であり、両方のアプローチに使用ケースがあります。finally のコードは常に実行されますか?try ブロックが return、break、continue を持っていても、finally は実行されます。唯一の例外は、プログラムが強制終了された場合です(例:os._exit() または停電)。これにより、finally はリソースクリーンアップの優れた場所となります。📖 まとめ
- 例外は実行時エラー。
try-exceptで処理 - 構造:
try→except(特定の例外型) →else(例外なし) →finally(常に実行) - よくある例外:
ValueError、TypeError、FileNotFoundError、ZeroDivisionError、IndexError、KeyError raiseで明示的に例外をスローし、呼び出し元にエラー処理を任せる- カスタム例外クラスは
Exceptionを継承し、エラー種別を正確に区別 loggingモジュールはprint()を置き換え、レベル制御、ファイル出力、タイムスタンプをサポート
📝 練習問題
-
初級(難易度 ⭐):ユーザーに 2 つの数値を入力させ、その除算を計算するプログラムを書いてください。
try-exceptでValueError(入力が数値でない)とZeroDivisionError(ゼロ除算)を、それぞれ親しみやすいエラーメッセージで処理してください。 -
中級(難易度 ⭐⭐):関数
read_int_from_file(filename)を書いてください。ファイルから最初の数値を読み取り、それを返します。try-except で以下を処理:ファイルが見つからない、ファイル内容が有効な数値でない、ファイルが空。各ケースで異なるエラーメッセージを返してください。 -
上級(難易度 ⭐⭐⭐):「セーフ電卓」プログラムを書いてください。ユーザーが式(例:
10 / 3)を入力し、プログラムが計算して結果を出力します。要件:1)try-exceptでゼロ除算、値エラー、型エラーを処理。2)loggingを使って各計算操作(INFO)とエラー(ERROR)をcalculator.logファイルに記録。3)exitで終了をサポート。ヒント: 式の評価にはeval()を使用(セキュリティ上の懸念に注意——この演習では許容)。



