Python Context Managers — with Statement and Custom Implementations
The Interview Question
What are context managers in Python? How do you create a custom one?
Expert Answer
A context manager is an object that defines setup and teardown actions for a block of code using the 'with' statement. When you write 'with open(file) as f:', Python calls __enter__ when entering the block (which opens the file and returns the file object) and __exit__ when leaving the block (which closes the file). The critical value proposition: __exit__ is called even if an exception occurs inside the block, guaranteeing cleanup. This is why context managers are superior to try/finally for resource management — they're more readable and harder to get wrong. You can create custom context managers two ways: class-based (implement __enter__ and __exit__) or function-based (use contextlib.contextmanager with a generator that yields once). The function-based approach is more concise for simple cases. Common uses beyond files: database connections, locks, temporary state changes, timing blocks, and transaction management.
Key Points to Hit in Your Answer
- __enter__ runs at the start of 'with', __exit__ runs at the end (even on exception)
- __exit__ receives exception info — return True to suppress the exception
- contextlib.contextmanager lets you write context managers as generators
- Guarantees cleanup even if exceptions occur — safer than try/finally
- Common uses: files, DB connections, locks, temp directories, transactions
- Multiple context managers can be combined: with open(a) as f1, open(b) as f2:
Code Example
# Class-based context manager
class DatabaseConnection:
def __init__(self, connection_string):
self.conn_str = connection_string
self.conn = None
def __enter__(self):
self.conn = connect(self.conn_str)
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
self.conn.rollback()
else:
self.conn.commit()
self.conn.close()
return False # don't suppress exceptions
with DatabaseConnection("postgres://...") as conn:
conn.execute("INSERT ...") # auto-commits or rollbacks
# Function-based with contextlib (simpler)
from contextlib import contextmanager
@contextmanager
def timer(label):
start = time.perf_counter()
try:
yield # code inside 'with' block runs here
finally:
elapsed = time.perf_counter() - start
print(f"{label}: {elapsed:.4f}s")
with timer("query"):
db.execute("SELECT ...")
What Interviewers Are Really Looking For
Show both implementation approaches — class-based and contextlib. The database connection example with automatic commit/rollback is compelling because it's a real-world pattern. Key insight: __exit__ receiving exception info and the ability to suppress exceptions by returning True shows you understand the full protocol.
Practice This Question with AI Grading
Reading about interview questions is a start — but practicing with real-time AI feedback is how you actually get better. Goliath Prep grades your answers instantly and tells you exactly what you're missing.
Start Practicing Free →