HomeGuidesPythonPython Context Managers Explained — with Statement & Custom Managers
🐍 Python

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.

Examifyr·2026·5 min read

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
Note: `with` calls __enter__ on entry and __exit__ on exit. The file object's __exit__ calls close().

__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))
Note: __exit__ receives three arguments: exception type, value, and traceback. Return True to suppress the exception; return False (or None) to let it propagate.

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 executes

contextlib.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
# Teardown
Note: Code before yield = __enter__. Code after yield (in finally) = __exit__. Always put cleanup in finally so it runs even on exception.

Multiple 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

← Previous
Python Lambda Functions Explained — map, filter, sorted & Limits
← All Python guides