Explain Codes LogoExplain Codes Logo

Multiple variables in a 'with' statement?

python
context-managers
python-3.10
best-practices
Nikita BarsukovbyNikita Barsukov·Mar 1, 2025
TLDR

You can use multiple context managers in a single with statement, separated with commas:

with open('input.txt') as f_input, open('output.txt', 'w') as f_output: f_output.write(f_input.read()) # Quick 'n easy file copy!

This elegantly opens two files simultaneously, managing them within a single context.

Grouping context managers in Python 3.10

From Python 3.10 onwards, multiple context managers can be grouped using parentheses. It makes code more readable when dealing with multiple contexts:

with ( open('input.txt') as f_input, open('output.txt', 'w') as f_output ): f_output.write(f_input.read()) # Group therapy for context managers!

This battles any ambiguity within the code and ergonomically aligns multiple contexts for an eye-pleasing view.

Handling exceptions and clearing resources

A with statement ensures that every __exit__() method is invoked, even if an __enter__() raises an exception. This makes certain that all resources are cleaned up, preventing potential resource leaks like a seasoned custodian.

Breathing life into contexts with ExitStack

For dynamic numbers of context managers, contextlib.ExitStack truly shines. It allows stacking contexts like a master card dealer on a casino table, with a surety of cleanup, irrespective of when resources are added to the stack.

from contextlib import ExitStack with ExitStack() as stack: files = [stack.enter_context(open(file_name)) for file_name in file_list] # Read 'em and weep, files!

With this, ExitStack takes responsibility for managing all files, ensuring they are properly cleaned up when you step outside the context.

Fine-tuning the parser in Python 3.9

Python 3.9 orchestrated signinificant improvements in parsing that resolved ambiguity when using parentheses with the with statement. The trade-off was great, with LL(1) parsers previously misidentifying multiple contexts as tuples; we could say Python got a new pair of glasses!

Back to the past: Pre-Python 3.10

Pre-Python 3.10, developers used backslashes to continue lines:

with open('file1.txt') as file1, \ open('file2.txt') as file2: pass # Do some fancy processing here

Though it's collecting dust now, it still retains validity for those nostalgic of the past!

Creating custom context managers with contextlib

Creating custom context managers using contextlib is as fun as making home-made pizza with your favorite toppings:

from contextlib import contextmanager @contextmanager def custom_resource(): # setup code yield resource # cleanup code with custom_resource() as cr: # Bon Appétit!

This allows for reusability and neat encapsulation of resource management logic.