Mid-Level Advanced Patterns 5 min read

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 →