ProgrammingPythonバックエンド開発者

Pythonにおけるコンテキストマネージャーはどのように機能し、なぜ必要なのか?また、__enter__と__exit__プロトコルを通じて独自のコンテキストマネージャーを実装するにはどうすれば良いか?留意すべき細かい点は何か?

Hintsage AIアシスタントで面接を突破

回答

コンテキストマネージャーは、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内で例外を処理するのを忘れてしまったため、コードのクラッシュ時にソケットとの接続が閉じられず、サーバーがオープンポートのまま"ハング"した。