Est-il possible de déclarer plus d'une variable en utilisant une instruction with
en Python?
Quelque chose comme:
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)
... ou le nettoyage de deux ressources en même temps est-il le problème?
C'est possible dans Python 3 depuis la v3.1 et Python 2.7 . La nouvelle with
syntaxe prend en charge plusieurs gestionnaires de contexte:
with A() as a, B() as b, C() as c:
doSomething(a,b,c)
Contrairement au contextlib.nested
, cela garantit que a
et b
verront leur __exit__()
appelée même si C()
ou sa méthode __enter__()
lève une exception.
contextlib.nested
supporte ceci:
_import contextlib
with contextlib.nested(open("out.txt","wt"), open("in.txt")) as (file_out, file_in):
...
_
Mise à jour:
Pour citer la documentation concernant contextlib.nested
:
Obsolète depuis la version 2.7 : L'instruction with prend désormais en charge cette fonctionnalité directement (sans les bizarreries sources de confusion).
Voir réponse de Rafał Dowgird pour plus d'informations.
Notez que si vous divisez les variables en lignes, vous devez utiliser des barres obliques inverses pour envelopper les nouvelles lignes.
with A() as a, \
B() as b, \
C() as c:
doSomething(a,b,c)
Les parenthèses ne fonctionnent pas car Python crée un Tuple à la place.
with (A(),
B(),
C()):
doSomething(a,b,c)
Comme les tuples n’ont pas d’attribut __enter__
, vous obtenez une erreur (non descriptive et n’identifiant pas le type de classe):
AttributeError: __enter__
Si vous essayez d'utiliser as
entre parenthèses, Python intercepte l'erreur au moment de l'analyse:
with (A() as a,
B() as b,
C() as c):
doSomething(a,b,c)
ErreurDeSyntaxe: Syntaxe invalide
https://bugs.python.org/issue12782 semble être lié à ce problème.
Je pense que vous voulez faire cela à la place:
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)
Depuis Python3.3, vous pouvez utiliser la classe ExitStack
du module contextlib
.
Il peut gérer un nombre dynamique dynamique, ce qui signifie qu'il sera particulièrement utile si vous ne savez pas combien de fichiers vous allez gérer.
Le cas d'utilisation canonique mentionné dans la documentation est la gestion d'un nombre dynamique de fichiers.
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
Voici un exemple générique:
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)
Sortie:
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]