¿Múltiples variables en una declaración 'con'?
¿ Es posible declarar más de una variable usando una with
declaración 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 el problema es limpiar dos recursos al mismo tiempo?
Es posible en Python 3 desde v3.1 y Python 2.7 . La nueva with
sintaxis admite múltiples administradores 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
se b
llamará a __exit__()
and incluso si C()
su __enter__()
método genera una excepción.
También puede utilizar variables anteriores en definiciones posteriores (h/t Ahmad a continuación):
with A() as a, B(a) as b, C(a, b) as c:
doSomething(a, c)
A partir de Python 3.10, puedes usar paréntesis :
with (
A() as a,
B(a) as b,
C(a, b) as c,
):
doSomething(a, c)
Tenga en cuenta que si divide las variables en líneas, antes de Python 3.10 debía 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.
with (A(),
B(),
C()):
doSomething(a,b,c)
Como las tuplas carecen de un __enter__
atributo, aparece un error (no descriptivo y no identifica el tipo de clase):
AttributeError: __enter__
Si intenta utilizar as
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)
SyntaxError: invalid syntax
¿Cuándo se va a arreglar esto?
Este problema se rastrea en https://bugs.python.org/issue12782 .
Python anunció en PEP 617 que reemplazarían el analizador original por uno nuevo. Debido a que el analizador original de Python es LL(1), no puede distinguir entre "administradores de contexto múltiples" with (A(), B()):
y "tupla de valores" with (A(), B())[0]:
.
El nuevo analizador puede analizar correctamente varios administradores de contexto entre paréntesis. El nuevo analizador se ha habilitado en 3.9. Se informó que esta sintaxis seguirá siendo rechazada hasta que se elimine el analizador antiguo en Python 3.10, y este cambio de sintaxis se informó en las notas de la versión 3.10 . Pero en mis pruebas, también funciona en Python 3.9.6 de trinket.io.
Desde Python 3.3, puedes usar la clase ExitStack
del contextlib
módulo.
Puede gestionar una cantidad dinámica de objetos contextuales, lo que significa que resultará especialmente útil si no sabes cuántos archivos vas a manejar.
El caso de uso canónico que se menciona en la documentación es la gestión de una cantidad dinámica 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)
Producción:
deque([])
enter X1
enter X2
enter X3
deque([<function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86158>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f861e0>, <function ExitStack._push_cm_exit.<locals>._exit_wrapper at 0x7f5c95f86268>])
exit X3
exit X2
exit X1
deque([])
[1, 2, 3]
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)
Desde Python 3.10 hay una nueva característica de administradores de contexto entre paréntesis , que permite sintaxis como:
with (
A() as a,
B() as b
):
do_something(a, b)