Python Context Managers: with, __enter__, __exit__ & contextlib
Context managers guarantee cleanup even when exceptions occur. Exams test __enter__/__exit__, exception suppression, and the contextlib shortcut.
The with statement
The `with` statement ensures setup and teardown code always runs — even if an exception is raised. The most common use is file handling.
# Without with — resource may not close if an exception occurs
f = open('data.txt', 'r')
data = f.read()
f.close() # skipped if read() raises an exception
# With with — close() is guaranteed
with open('data.txt', 'r') as f:
data = f.read()
# f is automatically closed here, even on exception__enter__ and __exit__
Any object with `__enter__` and `__exit__` methods is a context manager. `__enter__` returns the value bound to `as`; `__exit__` receives exception info.
class Timer:
import time
def __enter__(self):
self.start = __import__('time').time()
return self # bound to the 'as' variable
def __exit__(self, exc_type, exc_val, exc_tb):
elapsed = __import__('time').time() - self.start
print(f"Elapsed: {elapsed:.3f}s")
return False # do NOT suppress exceptions
with Timer() as t:
total = sum(range(1_000_000))Exception handling in __exit__
__exit__ is called even when an exception is raised inside the `with` block. Returning True suppresses it; returning False re-raises it.
class Suppress:
def __init__(self, *exc_types):
self.exc_types = exc_types
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# Return True to suppress the given exception types
return exc_type is not None and issubclass(exc_type, self.exc_types)
with Suppress(ZeroDivisionError):
x = 1 / 0 # exception is suppressed
print("Still running") # this line executescontextlib.contextmanager — the shortcut
Instead of writing a class, use `@contextmanager` with a generator function. `yield` marks where the `with` block executes.
from contextlib import contextmanager
@contextmanager
def managed_resource():
print("Setup") # __enter__ equivalent
try:
yield "resource" # value bound to 'as'
finally:
print("Teardown") # __exit__ equivalent
with managed_resource() as r:
print(f"Using {r}")
# Output:
# Setup
# Using resource
# TeardownMultiple context managers
You can open multiple context managers in one `with` statement by separating them with commas.
# Old way (nested with)
with open('input.txt') as fin:
with open('output.txt', 'w') as fout:
fout.write(fin.read())
# Modern way — single with, comma-separated
with open('input.txt') as fin, open('output.txt', 'w') as fout:
fout.write(fin.read())Exam tip
Key exam facts: (1) __exit__ is always called, even on exception; (2) returning True from __exit__ suppresses the exception; (3) the value after `as` comes from __enter__'s return value; (4) contextlib.contextmanager lets you write a context manager as a generator function.
Think you're ready? Prove it.
Take the free Python readiness test. Get a score, topic breakdown, and your exact weak areas.
Take the free Python test →Free · No sign-up · Instant results