Using the with statement, we can enter many context handlers using only one level of indentation/nesting:
>>> from contextlib import contextmanager
>>> @contextmanager
... def frobnicate(n):
... print('frobbing {}'.format(n))
... yield
...
>>> frob1 = frobnicate(1)
>>> frob2 = frobnicate(2)
>>> with frob1, frob2:
... pass
...
frobbing 1
frobbing 2
But this doesn't seem to work:
>>> frobs = [frobnicate(1), frobnicate(2)]
>>> with *frobs:
... pass
# SyntaxError: invalid syntax
How can we enter n context managers without having to manually write out each one?
python2.7 had contextlib.nested to do exactly that, however, it was deprecated due to error-prone quirks.
This function has two major quirks that have led to it being deprecated. Firstly, as the context managers are all constructed before the function is invoked, the
__new__()
and__init__()
methods of the inner context managers are not actually covered by the scope of the outer context managers. That means, for example, that usingnested()
to open two files is a programming error as the first file will not be closed promptly if an exception is thrown when opening the second file.Secondly, if the
__enter__()
method of one of the inner context managers raises an exception that is caught and suppressed by the__exit__()
method of one of the outer context managers, this construct will raise RuntimeError rather than skipping the body of the with statement.
python3.3 does a better job with contextlib.ExitStack which would look like:
from contextlib import ExitStack
with ExitStack() as stack:
contexts = [stack.enter_context(frobnicate(i)) for i in range(2)]
...
See contextlib2 for a backport to python2.x code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With