web-dev-qa-db-ja.com

Python __enter__ / __exit__ vs __init__(または__new__)/ __del__

私は検索しましたが、__enter__(または__exit__?)/ __init__ではなくpythonの__new__/__del__を使用する十分な理由を思い付くことができません。

__enter__/__exit__withステートメントをコンテキストマネージャーとして使用するためのものであり、withステートメントはすばらしいと理解しています。しかし、これに対応するのは、それらのブロック内のコードはonlyがそのコンテキストで実行されるということです。 __init__/__del__の代わりにこれらを使用することにより、withを使用する必要がある呼び出し元との暗黙のコントラクトを作成しているように見えますが、そのようなコントラクトを強制する方法はなく、コントラクトはドキュメント(またはコード)。それは悪い考えのようです。

withブロック内で__init__/__del__を使用しても同じ効果が得られるようです。しかし、コンテキスト管理メソッドではなくそれらを使用することで、私のオブジェクトは他のシナリオでも役立ちます。

それで、私がeverコンストラクタ/デストラクタメソッドではなくコンテキスト管理メソッドを使用したいという説得力のある理由を誰かが思い付くことができますか?

このような質問をするのにより良い場所がある場合は、私に知らせてください。しかし、これについての良い情報はあまりないようです。

フォローアップ:

私は常にwithを使用して新しいオブジェクトをインスタンス化するため、この質問は悪い(しかし一般的な可能性が高い)仮定に基づいていました。この場合、__init__/__del____enter__/__exit__(ただし、__del__をいつ実行するかを制御できない場合を除き、ガベージコレクション次第であり、プロセスが最初に終了した場合は呼び出されない場合があります)。しかし、withステートメントで既存のオブジェクトを使用する場合、それらはもちろんまったく異なります。

22
BobDoolittle

あなたが見逃しているように見えるいくつかの違いがあります:

  • コンテキストマネージャは、実行中のブロックだけに新しいオブジェクトを提供する機会を得ます。一部のコンテキストマネージャーはselfを返すだけです(ファイルオブジェクトと同じように)が、たとえば、データベース接続オブジェクトは現在のトランザクションに関連付けられたカーソルオブジェクトを返す場合があります。

  • コンテキストマネージャは、コンテキストの終了だけでなく、例外が原因で終了が発生した場合にも通知されます。次に、そのイベントの処理を決定するか、終了時に別の方法で反応します。もう一度例としてデータベース接続を使用すると、例外があることに基づいて、トランザクションをコミットまたは中止できます。

  • __del__は、オブジェクトへのall参照が削除された場合にのみ呼び出されます。つまり、その存続期間を制御する場合もしない場合もある複数の参照が必要な場合、その呼び出しに依存することはできません。ただし、コンテキストマネージャ出口は正確に定義されています。

  • コンテキストマネージャは再利用でき、状態を保持できます。再びデータベース接続。一度作成してから、コンテキストマネージャとして何度も使用すると、接続が開いたままになります。そのために毎回新しいオブジェクトを作成する必要はありません。

    これは、たとえばスレッドロックにとって重要です。あなたhaveは状態を保持し、一度に1つのスレッドのみがロックを保持できるようにします。これを行うには、oneロックオブジェクトを作成し、次にwith lock:を使用して、そのセクションを実行するさまざまなスレッドがそれぞれそのコンテキストに入る前に待機するようにします。 。

__enter__メソッドと__exit__メソッドは、コンテキストマネージャプロトコルを形成します。これらを実際に使用するのは、環境。コンテキストマネージャの目的は、単一のインスタンスの存続期間を管理することではなく、一般的なtry...finallyおよびtry...exceptパターンを簡略化することです。 PEP 343 –「with」ステートメント を参照してください:

このPEPは、Python言語に "with"という新しいステートメントを追加して、try/finallyステートメントの標準的な使用を除外できるようにします。

17
Martijn Pieters

_del x_はx.__del__()を直接呼び出しません

_.__del___がいつ呼び出されるか、または実際には まったく呼び出されるかどうか を制御することはできません。

したがって、コンテキスト管理に___init___/___del___を使用することは信頼できません。

5
wim

__init__/__del__の代わりにこれらを使用することで、呼び出し元との暗黙のコントラクトを作成しているように見えますが、withを使用する必要がありますが、そのようなコントラクトを強制する方法はありません

あなたはどちらかの方法で契約を結んでいます。ユーザーが使用後にクリーンアップが必要であることに気づかずにオブジェクトを使用すると、クリーンアップの実装方法に関係なく、ユーザーは物事を台無しにします。たとえば、__del__の実行を妨げるなど、オブジェクトへの参照を永久に保持する可能性があります。

特別なクリーンアップが必要なオブジェクトがある場合は、この要件を明示的にする必要があります。ユーザーにwith機能と明示的なcloseまたは同様のメソッドを提供して、クリーンアップの発生時期をユーザーが制御できるようにする必要があります。 __del__メソッド内でクリーンアップ要件を非表示にすることはできません。安全対策として__del__を実装することもできますが、__del__の代わりにwithまたは明示的なclose


そうは言っても、Pythonは__del__が実行されるという約束を一切しません。標準の実装は、オブジェクトの参照カウントが0に下がったときに__del__を実行しますが、参照がスクリプトの最後まで存続する場合、またはオブジェクトが参照サイクル内にある場合に発生します。他の実装では参照カウントを使用しないため、__del__の予測がさらに困難になります。