中学英語808712 views
Computer365120 views
高校日本史189857 views
雑学1472593 views
小学理科717236 views
中学理科1626207 views
英語607877 views
教育148875 views
世界の国560595 views
中学数学621382 views
Help
Tools

English

Python の __exit__ で例外を処理する

__exit__() メソッドは、with ブロック内で発生した例外の情報を受け取ります。この情報を使って、例外を処理したり、ログを記録したり、例外を抑制したりできます。

exit の引数

__exit__() は3つの引数を受け取ります。

def __exit__(self, exc_type, exc_val, exc_tb):
    pass
exc_type

例外のクラス(型)。例外がなければ None。

exc_val

例外のインスタンス(値)。例外がなければ None。

exc_tb

トレースバックオブジェクト。例外がなければ None。

例外情報の確認

例外が発生したかどうかは、exc_typeNone かどうかで判断できます。

class ExceptionLogger:
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            print("正常終了")
        else:
            print(f"例外発生: {exc_type.__name__}: {exc_val}")
        return False

# 正常終了の場合
with ExceptionLogger():
    print("処理中")
# 処理中
# 正常終了

# 例外発生の場合
with ExceptionLogger():
    raise ValueError("エラーです")
# 例外発生: ValueError: エラーです
# (その後 ValueError が再送出される)

例外の抑制

__exit__()True を返すと、例外が抑制されて外部に伝播しません。

class SuppressError:
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is ValueError:
            print(f"ValueError を抑制: {exc_val}")
            return True  # 例外を抑制
        return False  # その他の例外は再送出

with SuppressError():
    raise ValueError("無視される")
print("処理継続")
# ValueError を抑制: 無視される
# 処理継続
return True

例外を抑制する。with ブロックの外には伝播しない。

return False(またはなし)

例外を再送出する。with ブロックの外に伝播する。

特定の例外のみ処理

例外の型をチェックして、特定の例外だけを処理することもできます。

class HandleFileError:
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is FileNotFoundError:
            print(f"ファイルが見つかりません: {exc_val}")
            return True
        elif exc_type is PermissionError:
            print(f"権限がありません: {exc_val}")
            return True
        return False  # その他の例外はそのまま

with HandleFileError():
    open("存在しないファイル.txt")
print("処理継続")
# ファイルが見つかりません: [Errno 2] No such file or directory: '存在しないファイル.txt'
# 処理継続

クリーンアップは必ず実行される

例外を抑制するかどうかに関わらず、__exit__() 自体は必ず呼び出されます。

class CleanupDemo:
    def __enter__(self):
        print("リソース確保")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("リソース解放")  # 例外があっても実行される
        return False

try:
    with CleanupDemo():
        raise RuntimeError("エラー")
except RuntimeError:
    print("例外をキャッチ")

# リソース確保
# リソース解放
# 例外をキャッチ

注意点

例外を抑制する場合は慎重に行いましょう。予期しない例外まで抑制してしまうと、バグの発見が困難になります。通常は特定の例外のみを抑制し、それ以外は再送出するのが安全です。