Pergunta

Essa pergunta já tem resposta aqui:

Suponha que você tenha três objetos adquiridos por meio do gerenciador de contexto, por exemplo, um bloqueio, uma conexão db e um soquete IP.Você pode adquiri-los:

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

Mas existe uma maneira de fazer isso em um bloco?algo como

with lock,db_con,socket:
   #do stuff

Além disso, é possível, dada uma matriz de comprimento desconhecido de objetos que possuem gerenciadores de contexto, é possível fazer de alguma forma:

a=[lock1, lock2, lock3, db_con1, socket, db_con2]
with a as res:
    #now all objects in array are acquired

Se a resposta for "não", é porque a necessidade de tal recurso implica um design ruim, ou talvez eu deva sugeri-lo de forma estimulante?:-P

Foi útil?

Solução

Em Python 2.7 e 3.1 e superior, você pode escrever:

with A() as X, B() as Y, C() as Z:
    do_something()

Normalmente, esse é o melhor método a ser usado, mas se você tiver uma lista de gerenciadores de contexto de tamanho desconhecido, precisará de um dos métodos abaixo.


Em Pitão 3.3, você pode inserir uma lista de gerenciadores de contexto de tamanho desconhecido usando contextlib.ExitStack:

with ExitStack() as stack:
    for mgr in ctx_managers:
        stack.enter_context(mgr)
    # ...

Isso permite que você crie os gerenciadores de contexto à medida que os adiciona ao ExitStack, o que evita o possível problema com contextlib.nested (mencionado abaixo).

contextolib2 fornece um backport de ExitStack para Python 2.6 e 2.7.


Em Python 2.6 e abaixo, você pode usar contextlib.nested:

from contextlib import nested

with nested(A(), B(), C()) as (X, Y, Z):
    do_something()

é equivalente a:

m1, m2, m3 = A(), B(), C()
with m1 as X:
    with m2 as Y:
        with m3 as Z:
            do_something()

Observe que isso não é exatamente o mesmo que normalmente usar aninhados with, porque A(), B(), e C() todos serão chamados inicialmente, antes de entrar nos gerenciadores de contexto.Isto não funcionará corretamente se uma dessas funções gerar uma exceção.

contextlib.nested está obsoleto nas versões mais recentes do Python em favor dos métodos acima.

Outras dicas

A primeira parte da sua pergunta é possível em Pitão 3.1.

Com mais de um item, os gerenciadores de contexto são processados ​​como se múltiplas instruções with estivessem aninhadas:

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

é equivalente a

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

Alterado na versão 3.1:Suporte para múltiplas expressões de contexto

A resposta de @interjay está correta.No entanto, se você precisar fazer isso para gerenciadores de contexto longos, por exemplo, gerenciadores de contexto mock.patch, você rapidamente perceberá que deseja dividir isso entre linhas.Acontece que você não pode colocá-los entre parênteses, então você tem que usar barras invertidas.Aqui está o que parece:

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

A segunda parte da sua pergunta é resolvida com contextlib.ExitStack em Pitão 3.3.

Seguindo a resposta do @ sage88, você sempre pode atribuir a esses patches nomes de variáveis ​​​​significativos antes de inseri-los.

Você poderia criar esses patches em várias linhas

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()
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top