web-dev-qa-db-ja.com

「as」キーワードのない「with」ステートメントの意味

例外がスローされた場合にオブジェクトを確実にファイナライズする手段としてpythonのwithステートメントを使用することに慣れています。これは通常次のようになります

with file.open('myfile.txt') as f:
    do stuff...

これは略記です

f = file.open('myfile.txt'):
try:
    do stuff...
finally:
    f.close()

または、クラスが提示する可能性のあるその他のファイナライズルーチン。

私は最近、これを提示するOpenGLを処理するコードに遭遇しました。

with self.shader:
    (Many OpenGL commands)

asキーワードがないことに注意してください。これは__enter__および__exit__クラスのメソッドはまだ呼び出されますが、オブジェクトがブロック内で明示的に使用されることはありません(つまり、オブジェクトはグローバルまたは暗黙的な参照を通じて機能します)?それとも私を避けている他の意味がありますか?

36
KDN

コンテキストマネージャはオプションを使用してオブジェクトを返し、asで指定された識別子に割り当てることができます。そして、asによって割り当てられるのは___enter___メソッドによって返されるオブジェクトであり、必ずしもコンテキストマネージャ自体ではありません。

_as <identifier>_を使用すると、open()呼び出しのようにnewオブジェクトを作成するときに役立ちますが、すべてのコンテキストマネージャーがコンテキストだけで作成されるわけではありません。たとえば、再利用可能で、すでに作成されている場合があります。

データベース接続を取ります。データベース接続は1回だけ作成しますが、多くのデータベースアダプターでは、接続をコンテキストマネージャーとして使用できます。コンテキストを入力してトランザクションを開始し、トランザクションを終了してトランザクションをコミット(成功時)またはロールバック(例外がある場合)します。

_with db_connection:
    # do something to the database
_

ここで新しいオブジェクトを作成する必要はありません。コンテキストはdb_connection.__enter__()で入力し、db_connection.__exit__()で再び終了しますが、すでにhavea接続オブジェクトへの参照。

これでできます入力すると、接続オブジェクトがカーソルオブジェクトを生成します。ここで、そのカーソルオブジェクトをローカル名で割り当てることには意味があります。

_with db_connection as cursor:
    # use cursor to make changes to the database
_

_db_connection_はまだここでは呼び出されず、以前から存在しており、すでに参照されています。しかし、生成されたdb_connection.__enter__()はすべてcursorに割り当てられ、そこからそのまま使用できます。

これは、ファイルオブジェクトで発生することです。 open()はファイルオブジェクトを返し、fileobject.__enter__()はファイルオブジェクトitselfを返すため、open()を使用できますwithステートメントを呼び出しおよび新しく作成されたオブジェクトへの参照を2つではなく1つのステップで割り当てます。その小さなトリックがなければ、以下を使用する必要があります。

_f = open('myfile.txt')
with f:
    # use `f` in the block
_

これらすべてをシェーダーの例に適用します。 _self.shader_への参照はすでにあります。 self.shader.__enter__()が_self.shader_への参照を再び返す可能性は非常に高いですが、完全にサービス可能な参照が既にあるので、なぜそのための新しいローカルを作成するのですか?

38
Martijn Pieters