Создайте «с» блоком на нескольких контекстных менеджерах? [Дубликат

StackOverflow https://stackoverflow.com/questions/3024925

Вопрос

Этот вопрос уже имеет ответ здесь:

Предположим, у вас есть три объекта, которые вы приобретете через Context Manager, например, замок, соединение DB и сокет IP. Вы можете приобрести их по:

with lock:
   with db_con:
       with socket:
            #do stuff

Но есть ли способ сделать это в одном блоке? что-то вроде

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? :-П

Это было полезно?

Решение

В 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 (упомянуто ниже).

contextlib2. предоставляет Заклинание 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 в пользу вышеуказанных методов.

Другие советы

Первая часть вашего вопроса возможна в Python 3.1..

С более чем одним предметом контекстные менеджеры обрабатываются так, как будто несколько с заявлениями были вложены:

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

эквивалентно

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

Изменено в версии 3.1: Поддержка нескольких контекстных выражений

@ Ответ Interjay является правильным. Однако, если вам нужно сделать это для длинных контекстных менеджеров, например, Mock.patch Conturext Managers, вы быстро понимаете, что хотите сломать это по линиям. Оказывается, вы не можете обернуть их в Parens, поэтому вы должны использовать обратные капли. Вот что выглядит так:

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

Вторая часть вашего вопроса решается с contextlib.ExitStack в Python 3.3..

Следуя по ответу @ 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()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top