複数のコンテキスト マネージャーに「with」ブロックを作成しますか? [重複] 質問する

複数のコンテキスト マネージャーに「with」ブロックを作成しますか? [重複] 質問する

コンテキスト マネージャー経由で取得する 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提供するのバックポートExitStackPython 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 では上記のメソッドが推奨されるため、非推奨となっています。

おすすめ記事