python – Multiple variables in a with statement?

python – Multiple variables in a with statement?

It is possible in Python 3 since v3.1 and Python 2.7. The new with syntax supports multiple context managers:

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

Unlike the contextlib.nested, this guarantees that a and b will have their __exit__()s called even if C() or its __enter__() method raises an exception.

You can also use earlier variables in later definitions (h/t Ahmad below):

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

As of Python 3.10, you can use parentheses:

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

Note that if you split the variables into lines, prior to Python 3.10 you must use backslashes to wrap the newlines.

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

Parentheses dont work, since Python creates a tuple instead.

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

Since tuples lack a __enter__ attribute, you get an error (undescriptive and does not identify class type):

AttributeError: __enter__

If you try to use as within parentheses, Python catches the mistake at parse time:

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

When will this be fixed?

This issue is tracked in https://bugs.python.org/issue12782.

Python announced in PEP 617 that they would replace the original parser with a new one. Because Pythons original parser is LL(1), it cannot distinguish between multiple context managers with (A(), B()): and tuple of values with (A(), B())[0]:.

The new parser can properly parse multiple context managers surrounded by parentheses. The new parser has been enabled in 3.9. It was reported that this syntax will still be rejected until the old parser is removed in Python 3.10, and this syntax change was reported in the 3.10 release notes. But in my testing, it works in trinket.ios Python 3.9.6 as well.

python – Multiple variables in a with statement?

contextlib.nested supports this:

import contextlib

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

   ...

Update:
To quote the documentation, regarding contextlib.nested:

Deprecated since version 2.7: The with-statement now supports this
functionality directly (without the confusing error prone quirks).

See RafaƂ Dowgirds answer for more information.

Leave a Reply

Your email address will not be published. Required fields are marked *