コンテキストマネージャーは、withブロックへのエントリおよびエグジットの動作を定義し、リソース(ファイル、接続など)の自動管理を提供するオブジェクトです。クラス内の__enter__および__exit__メソッドを使用して実装されるか、contextlibモジュールの@contextmanagerデコレーターを使用します。
class FileManager: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = None def __enter__(self): self.file = open(self.filename, self.mode) return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() with FileManager('test.txt', 'w') as f: f.write('Hello')
細かい点: __exit__で例外を正しく処理し、エラーを抑えるためにTrueを返すことが重要であり、エラー時にリソースが開いたままにならないように注意する必要があります。
質問: withブロック内で例外が発生した場合、Pythonは__exit__メソッドを呼び出しますか?エラーのパラメーターはどのように渡されますか?
回答: はい、__exit__メソッドは常に呼び出され、例外が発生した場合でも呼ばれます。この時、例外の型、値、およびトレースバックが渡されます。__exit__がTrueを返すと、例外は抑制されます。
class Simple: def __enter__(self): print('Enter') def __exit__(self, exc_type, exc_val, exc_tb): print('Exit') print(exc_type, exc_val) return True # エラーは外に "飛び出さない" with Simple(): raise ValueError('boom!')
物語
__exit__を実装し忘れた—例外が発生した場合、ファイルが閉じられず、ファイルディスクリプタが漏れ、膨大な量のファイルで作業するときに失敗することになった。物語
__exit__がTrueを返すことがあり、これによりエラーが"隠れ"、目に見えない失敗とデータ破損を引き起こした。物語
contextlibモジュールから@contextmanagerデコレーターを使用したが、yield内で例外を処理するのを忘れてしまったため、コードのクラッシュ時にソケットとの接続が閉じられず、サーバーがオープンポートのまま"ハング"した。