¿Múltiples variables en una statement ‘con’?

¿Es posible declarar más de una variable usando una statement with en Python?

Algo como:

 from __future__ import with_statement with open("out.txt","wt"), open("in.txt") as file_out, file_in: for line in file_in: file_out.write(line) 

… o está limpiando dos recursos al mismo tiempo que el problema?

Es posible en Python 3 desde v3.1 y Python 2.7 . El nuevo with syntax soporta múltiples gestores de contexto:

 with A() as a, B() as b, C() as c: doSomething(a,b,c) 

A diferencia de contextlib.nested , esto garantiza que a y b tendrán su __exit__() llamado incluso si C() o su método __enter__() genera una excepción.

contextlib.nested apoya esto:

 import contextlib with contextlib.nested(open("out.txt","wt"), open("in.txt")) as (file_out, file_in): ... 

Actualizar:
Para citar la documentación, referente a contextlib.nested :

En desuso desde la versión 2.7 : el with-statement ahora admite esta funcionalidad directamente (sin las confusas peculiaridades propensas al error).

Ver la respuesta de Rafał Dowgird para más información.

Creo que quieres hacer esto en su lugar:

 from __future__ import with_statement with open("out.txt","wt") as file_out: with open("in.txt") as file_in: for line in file_in: file_out.write(line) 

Tenga en cuenta que si divide las variables en líneas, debe usar barras invertidas para ajustar las nuevas líneas.

 with A() as a, \ B() as b, \ C() as c: doSomething(a,b,c) 

Los paréntesis no funcionan, ya que Python crea una tupla en su lugar.

 with (A(), B(), C()): doSomething(a,b,c) 

Como las tuplas carecen de un atributo __enter__ , __enter__ un error (no __enter__ y no identifica el tipo de clase):

 AttributeError: __enter__ 

Si intentas usarlo entre paréntesis, Python detecta el error en el momento del análisis:

 with (A() as a, B() as b, C() as c): doSomething(a,b,c) 

Error de syntax: syntax invalida

https://bugs.python.org/issue12782 parece estar relacionado con este problema.

Desde Python 3.3, puede usar la clase ExitStack del módulo contextlib .

Puede administrar un número dynamic de objetos sensibles al contexto, lo que significa que será especialmente útil si no sabe cuántos archivos va a manejar.

El caso de uso canónico que se menciona en la documentación es administrar un número dynamic de archivos.

 with ExitStack() as stack: files = [stack.enter_context(open(fname)) for fname in filenames] # All opened files will automatically be closed at the end of # the with statement, even if attempts to open files later # in the list raise an exception 

Aquí hay un ejemplo genérico:

 from contextlib import ExitStack class X: num = 1 def __init__(self): self.num = X.num X.num += 1 def __repr__(self): cls = type(self) return '{cls.__name__}{self.num}'.format(cls=cls, self=self) def __enter__(self): print('enter {!r}'.format(self)) return self.num def __exit__(self, exc_type, exc_value, traceback): print('exit {!r}'.format(self)) return True xs = [X() for _ in range(3)] with ExitStack() as stack: print(stack._exit_callbacks) nums = [stack.enter_context(x) for x in xs] print(stack._exit_callbacks) print(stack._exit_callbacks) print(nums) 

Salida:

 deque([]) enter X1 enter X2 enter X3 deque([._exit_wrapper at 0x7f5c95f86158>, ._exit_wrapper at 0x7f5c95f861e0>, ._exit_wrapper at 0x7f5c95f86268>]) exit X3 exit X2 exit X1 deque([]) [1, 2, 3]