Context Managers and `with`
Python · Core Language · Run with python3
Explanation
The `with` statement guarantees cleanup code runs even if exceptions occur. Any object implementing `__enter__` / `__exit__` is a context manager. The `contextlib.contextmanager` decorator lets you write generator-based context managers — `yield` is the body of the `with` block. `contextlib.ExitStack` composes a dynamic number of context managers.
Real-World Scenario
Database transactions: `with db.transaction():` commits on success, rolls back on any exception — no `try/finally` boilerplate needed. Opening multiple files simultaneously: `with open(a) as f1, open(b) as f2:` ensures both are closed even if the second `open` raises. ```python import contextlib import time @contextlib.contextmanager def timed(label: str): start = time.perf_counter() try: yield finally: elapsed = time.perf_counter() - start print(f"{label}: {elapsed:.4f}s") @contextlib.contextmanager def managed_resource(name: str): print(f"Acquiring {name}") try: yield name.upper() finally: print(f"Releasing {name}") with timed("demo"): with managed_resource("database") as res: print(f"Using {res}") ```
Quick Snippet
import contextlib
import time
@contextlib.contextmanager
def timed(label: str):
start = time.perf_counter()
try:
yield
finally:
elapsed = time.perf_counter() - start
print(f"{label}: {elapsed:.4f}s")
@contextlib.contextmanager
def managed_resource(name: str):
print(f"Acquiring {name}")
try:
yield name.upper()
finally:
print(f"Releasing {name}")
with timed("demo"):
with managed_resource("database") as res:
print(f"Using {res}")Full Example
# Compile: python3 context_managers.py
# Output:
# Acquiring database
# Using DATABASE
# Releasing database
import contextlib
import time
@contextlib.contextmanager
def timed(label: str):
start = time.perf_counter()
try:
yield
finally:
elapsed = time.perf_counter() - start
print(f"{label}: {elapsed:.4f}s")
@contextlib.contextmanager
def managed_resource(name: str):
print(f"Acquiring {name}")
try:
yield name.upper()
finally:
print(f"Releasing {name}")
with managed_resource("database") as res:
print(f"Using {res}")
# __enter__ / __exit__ directly
class TempDirectory:
def __enter__(self):
import tempfile, os
self.path = tempfile.mkdtemp()
return self.path
def __exit__(self, exc_type, exc_val, exc_tb):
import shutil
shutil.rmtree(self.path, ignore_errors=True)
return False # Don't suppress exceptions
with TempDirectory() as tmpdir:
print(f"Temp dir: {tmpdir}")
print("Temp dir cleaned up")
python3 context_managers.py