¿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]