コンテキスト マネージャー経由で取得する 3 つのオブジェクト (ロック、DB 接続、IP ソケットなど) があるとします。これらのオブジェクトは次のように取得できます。
with lock:
with db_con:
with socket:
#do stuff
しかし、それを1つのブロックで実行する方法はありますか?
with lock,db_con,socket:
#do stuff
さらに、コンテキスト マネージャーを持つ長さが不明なオブジェクトの配列が与えられた場合、次の操作を実行することは可能ですか。
a=[lock1, lock2, lock3, db_con1, socket, db_con2]
with a as res:
#now all objects in array are acquired
答えが「いいえ」の場合、そのような機能が必要であるということはデザインが悪いことを意味するのでしょうか、それとも私がそれを pep で提案する必要があるのでしょうか? :-P
ベストアンサー1
Python 2.7 および 3.1 以降では、次のように記述できます。
with A() as X, B() as Y, C() as Z:
do_something()
これは通常使用するのに最適な方法ですが、コンテキスト マネージャーのリストの長さが不明な場合は、以下のいずれかの方法が必要になります。
Python 3.3では、長さが不明なコンテキストマネージャのリストを入力するには、contextlib.ExitStack
:
with ExitStack() as stack:
for mgr in ctx_managers:
stack.enter_context(mgr)
# ...
これにより、 に追加するときにコンテキスト マネージャーを作成できるようになりExitStack
、(以下で説明する) で発生する可能性のある問題を回避できますcontextlib.nested
。
コンテキストライブラリ2提供するのバックポートExitStack
Python 2.6 および 2.7 の場合。
Python 2.6以下では、contextlib.nested
:
from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
do_something()
は以下と同等です:
m1, m2, m3 = A(), B(), C()
with m1 as X:
with m2 as Y:
with m3 as Z:
do_something()
これは、ネストされた を通常使用するのとまったく同じではないことに注意してください。with
これは、 A()
、B()
、および がC()
すべて、コンテキスト マネージャーに入る前に最初に呼び出されるためです。これらの関数のいずれかが例外を発生させる場合、これは正しく機能しません。
contextlib.nested
新しいバージョンの Python では上記のメソッドが推奨されるため、非推奨となっています。