¿Crear un bloque “con” en varios gestores de contexto?

Supongamos que tiene tres objetos que adquiere a través del administrador de contexto, por ejemplo, un locking, una conexión db y un socket ip. Puedes adquirirlos por:

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

¿Pero hay una manera de hacerlo en un bloque? algo como

 with lock,db_con,socket: #do stuff 

Además, si es posible, dado un conjunto de objetos de longitud desconocida que tienen administradores de contexto, es posible hacerlo de alguna manera:

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

Si la respuesta es “no”, ¿es porque la necesidad de tal característica implica un mal diseño, o tal vez debería sugerirlo de una manera? :-PAG

En Python 2.7 y 3.1 y superiores , puedes escribir:

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

Normalmente este es el mejor método para usar, pero si tiene una lista de administradores de contexto de longitud desconocida, necesitará uno de los siguientes métodos.


En Python 3.3 , puedes ingresar a una lista de administradores de contexto de longitud desconocida usando contextlib.ExitStack :

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

Esto le permite crear los administradores de contexto a medida que los agrega a ExitStack , lo que evita el posible problema con contextlib.nested (mencionado a continuación).

contextlib2 proporciona un backport de ExitStack para Python 2.6 y 2.7.


En Python 2.6 y más abajo , puedes usar contextlib.nested :

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

es equivalente a:

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

Tenga en cuenta que esto no es exactamente lo mismo que usar normalmente nested with , ya que A() , B() y C() se llamarán inicialmente, antes de ingresar a los administradores de contexto. Esto no funcionará correctamente si una de estas funciones genera una excepción.

contextlib.nested está en desuso en las versiones más nuevas de Python en favor de los métodos anteriores.

La primera parte de tu pregunta es posible en Python 3.1 .

Con más de un elemento, los administradores de contexto se procesan como si se anidaran múltiples con declaraciones:

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

es equivalente a

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

Cambiado en la versión 3.1 : Soporte para múltiples expresiones de contexto

La respuesta de @interjay es correcta. Sin embargo, si necesita hacer esto para los administradores de contexto largos, por ejemplo, los administradores de contexto de mock.patch, rápidamente se da cuenta de que desea dividir esto entre líneas. Resulta que no puedes envolverlos en parens, así que tienes que usar barras invertidas. Esto es lo que parece:

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

La segunda parte de su pregunta se resuelve con contextlib.ExitStack en Python 3.3 .

A partir de la respuesta de @sage88, siempre puede asignar esos parches para que tengan nombres de variables significativos antes de ingresar a ellos.

Podrías crear esos parches en múltiples líneas.

 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()