例外処理

プログラムの実行中には予期しない状況が常に発生します——ファイルが存在しない、ネットワークが切断される、ユーザーが無効なデータを入力する……これらの「例外」を処理しないと、プログラムはクラッシュします。例外処理により、プログラムがエラーから回復し、不可解なエラーメッセージの代わりにユーザーフレンドリーなプロンプトを表示できるようになります。


1. 例外とは

例外とは、プログラムの実行中に発生するエラーです。例外が処理されないと、プログラムは終了します:

PYTHON
# このコードはクラッシュする
# print(10 / 0)               # ZeroDivisionError: division by zero
# int("abc")                  # ValueError: invalid literal for int()
# open("non_existent.txt")    # FileNotFoundError

try-except を使ってこれらのエラーを適切に処理します:

例:try-except での除算と入力エラー処理

PYTHON
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!")
▶ 試してみよう

実行例:

TEXT
Enter a number: abc
That's not a valid number!
💡 例外処理の哲学: try を「考えられるすべてのエラーを予測する」ために使うのではなく、「まず実行し、問題が発生したら対処する」ために使います。Python コミュニティではこれを 「許しを請うことは、許可を得ることより簡単だ」(EAFP) と呼びます。


2. 完全な try-except 構造

例:try-except-else-finally 完全構造

PYTHON
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. よくある例外の種類

PYTHON
# ZeroDivisionError — ゼロによる除算
# int("abc")                # ValueError — 無効な値
# [1, 2][5]                 # IndexError — 範囲外のインデックス
# {"a": 1}["b"]             # KeyError — 存在しないキー
# open("x.txt")             # FileNotFoundError — ファイルが見つからない
# 10 + "hello"              # TypeError — 型エラー
# import nonexistent        # ModuleNotFoundError — モジュールが見つからない

すべての例外をキャッチする(推奨しない)

例:例外キャッチの正しい方法と間違った方法

PYTHON
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 で例外を発生

PYTHON
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 によるパスワード検証

PYTHON
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. カスタム例外クラス

より正確なエラー情報を提供するために、独自の例外型を作成できます:

例:銀行引き出しのカスタム例外

PYTHON
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 の設定と使用

PYTHON
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!")    # 致命的エラー
▶ 試してみよう

出力(ファイルとコンソールの両方に書き込まれる):

TEXT
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() 文を削除する必要がない。


よくあるユースケース


❓ よくある質問

Q except Exception as eas e は何を意味しますか?
A as e はキャッチした例外オブジェクトを変数 e に代入し、例外の詳細を調べられるようにします——print(e) でエラーの説明を表示、type(e).__name__ で例外の型名を表示。as e がなくても例外はキャッチできますが、詳細なエラー情報は見られません。
Q 例外と if チェックはいつ使い分ければよいですか?
A 事前に予測できるエラー(ゼロ除算のチェックなど)には if を使います。予測できないエラー(ネットワークリクエスト、ファイル操作など)には try-except を使います。Python コミュニティの EAFP スタイルは try の使用を好みます——しかしこれは美的な問題であり、両方のアプローチに使用ケースがあります。
Q finally のコードは常に実行されますか?
A ほぼ常に——try ブロックが returnbreakcontinue を持っていても、finally は実行されます。唯一の例外は、プログラムが強制終了された場合です(例:os._exit() または停電)。これにより、finally はリソースクリーンアップの優れた場所となります。

📖 まとめ


📝 練習問題

  1. 初級(難易度 ⭐):ユーザーに 2 つの数値を入力させ、その除算を計算するプログラムを書いてください。try-exceptValueError(入力が数値でない)と ZeroDivisionError(ゼロ除算)を、それぞれ親しみやすいエラーメッセージで処理してください。

  2. 中級(難易度 ⭐⭐):関数 read_int_from_file(filename) を書いてください。ファイルから最初の数値を読み取り、それを返します。try-except で以下を処理:ファイルが見つからない、ファイル内容が有効な数値でない、ファイルが空。各ケースで異なるエラーメッセージを返してください。

  3. 上級(難易度 ⭐⭐⭐):「セーフ電卓」プログラムを書いてください。ユーザーが式(例:10 / 3)を入力し、プログラムが計算して結果を出力します。要件:1)try-except でゼロ除算、値エラー、型エラーを処理。2)logging を使って各計算操作(INFO)とエラー(ERROR)を calculator.log ファイルに記録。3)exit で終了をサポート。ヒント: 式の評価には eval() を使用(セキュリティ上の懸念に注意——この演習では許容)。

100%