web-dev-qa-db-ja.com

複数のコンテキストマネージャーで「with」ブロックを作成しますか?

たとえば、ロック、db接続、ipソケットなど、コンテキストマネージャーを介して取得する3つのオブジェクトがあるとします。次の方法で取得できます。

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

答えが「いいえ」の場合、そのような機能の必要性が悪い設計を暗示しているからでしょうか、それとも私はそれを一言で提案するべきでしょうか? :-P

173
olamundo

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_(以下で説明)で問題が発生する可能性がなくなります。

contextlib2ExitStackのバックポート 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()
_

これは、A()B()、およびC()がすべて最初に呼び出されるため、ネストされたwithを通常使用する場合とまったく同じではないことに注意してください。 、コンテキストマネージャに入る前。これらの関数のいずれかが例外を発生させた場合、これは正しく機能しません。

_contextlib.nested_は、上記のメソッドを支持する新しいバージョンPythonバージョンでは非推奨です。

302
interjay

あなたの質問の最初の部分は Python 3.1 で可能です。

複数のアイテムがある場合、コンテキストマネージャーは複数のwithステートメントがネストされているかのように処理されます。

with A() as a, B() as b:
    suite

に等しい

with A() as a:
    with B() as b:
        suite

バージョン3.1で変更されました:複数のコンテキスト式のサポート

23
Mark Byers

@interjayの答えは正しいです。ただし、長いコンテキストマネージャー(mock.patchコンテキストマネージャーなど)でこれを行う必要がある場合は、これを複数行に分割する必要があることにすぐに気付きます。括弧で囲むことはできないため、バックスラッシュを使用する必要があります。これは次のようなものです。

with mock.patch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') as a, \
        mock.patch('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') as b, \
        mock.patch('cccccccccccccccccccccccccccccccccccccccccc') as c:
    do_something()
17
sage88

質問の2番目の部分は、contextlib.ExitStack in Python 3.

8
Neil G

@ sage88の応答に続いて、入力する前に、それらのパッチにいつでも意味のある変数名を割り当てることができます。

これらのパッチを複数行で作成できます

a_patch = mock.patch('aaaaaaa') 
b_patch = mock.patch('bbbbbbb')
c_patch = mock.patch('ccccccc') 
with a_patch as a, b_patch as b, as c:    
    do_something()
0
poulter7