2010-06-11 18 views
117

Supongamos que tiene tres objetos que adquiere a través del gestor de contexto, por ejemplo, un bloqueo, una conexión db y un socket ip. Puede adquirirlos por:python: cree un bloque "con" en varios gestores de contexto

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

pero hay una manera de hacerlo en un bloque? algo así como

with lock,db_con,socket: 
    #do stuff 

Por otra parte, es posible que, dada una matriz de longitud desconocida de objetos que tienen gestores de contexto, es posible de alguna manera hacer:

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 en un pep? :-P

+1

posible duplicado de [Múltiples variables en Python 'con' declaración] (http://stackoverflow.com/questions/893333/multiple-variables-in-python-with-statement) –

Respuesta

212

En Python 2.6 y continuación, puede utilizar 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 el mismo que normalmente utilizando anidada with, porque 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 puede generar excepciones, pero funcionará para los ejemplos en la pregunta.


En Python 2.7 y 3.1, la sintaxis se ha añadido para esto, y contextlib.nested ha quedado obsoleto:

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

En Python 3.3, también puede introducir un desconocido -length list of context managers using contextlib.ExitStack:

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

Esto le permite crear los administradores de contexto al agregarlos al ExitStack, lo que evita el posible problema con contextlib.nested.

contextlib2 proporciona a backport of ExitStack para Python 2.6 y 2.7.

+0

gracias! Entonces puedo usar esto para una variedad de administradores de contexto con 'contextlib.nested (* arr)'.
¿Es esto posible de alguna manera en la nueva sintaxis de python 2.7 y 3.1? – olamundo

+2

@noam: No, de hecho, la docstring para 'anidado' en 3.1 dice:" La única ventaja de esta función sobre la forma de administrador múltiple de la sentencia con es que el desempaquetado de argumentos permite su uso con una cantidad variable de administradores de contexto de la siguiente manera: 'con jerarquizados (* administradores): do_something()' " – interjay

+9

Es extraño, por un lado, está en desuso, pero por el otro reconocen una ventaja del módulo en desuso sobre el reemplazo? – olamundo

18

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

Con más de un artículo, los gestores de contexto se procesan como si instrucciones múltiples se anida:

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: Apoyo para expresiones de contexto múltiples

+0

gracias! pero eso todavía no respondía a toda mi pregunta: ¿qué pasa con el segundo caso que mencioné, donde los administradores de contexto se dan en una matriz, sin saber cuántos pesebres hay en la matriz. ¿Será posible en algún python3.X hacer 'con [cm1, cm2, cm3, cm4, cm5] como resultado: ....' – olamundo

+2

@noam: Para resolver la segunda parte de su pregunta, podría escribir una clase para envolver una cantidad de recursos e implementar '__enter__' y' __exit__' para esa clase. No estoy seguro de si hay una clase de biblioteca estándar que ya lo haga. –

+0

@Mark No creo que sea tan fácil, es por eso que 'contextlib.nested()' está en desuso. Si ocurre algo entre la generación de las otras cosas y la activación del administrador de contexto, puede suceder que la limpieza no se realice como se desea. – glglgl

7

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

1

@ interjay's La respuesta es correcta. Sin embargo, si necesita hacer esto para administradores de contexto largos, por ejemplo administradores de contexto de mock.patch, entonces se da cuenta rápidamente de que desea dividir esto entre líneas. Resulta que no puedes envolverlos en paréntesis, por lo que debes usar barras diagonales inversas. Esto es lo que parece:

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