🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
REFERENCEpython

python Documentation

LOADING ENGINE...

Context Managers (@contextmanager)

AI & DATA SCIENCE // context-managers-contextmanager

The @contextmanager decorator from contextlib lets you write a context manager (for use with `with`) as a single generator function, instead of a full class with __enter__ and __exit__.

Syntax

from contextlib import contextmanager

@contextmanager
def my_context():
    setup()
    yield value
    teardown()

Deep Dive Course

A function decorated with @contextmanager runs its code up to the yield statement as the setup phase, equivalent to __enter__, whatever it yields becomes the value bound by `as` in the with statement, and the code after yield runs as the teardown phase, equivalent to __exit__, once the with block finishes, including if an exception was raised inside the block, which shows up as an exception raised at the yield point itself, letting you wrap it in a try/finally to guarantee cleanup either way. This is often a much more concise way to write a simple context manager than defining a full class with two separate dunder methods.

1Understanding Context Managers (@contextmanager)

A function decorated with @contextmanager runs its code up to the yield statement as the setup phase, equivalent to __enter__, whatever it yields becomes the value bound by as in the with statement, and the code after yield runs as the teardown phase, equivalent to __exit__, once the with block finishes, including if an exception was raised inside the block, which shows up as an exception raised at the yield point itself, letting you wrap it in a try/finally to guarantee cleanup either way. This is often a much more concise way to write a simple context manager than defining a full class with two separate dunder methods.

💡

Wrap the yield in a try/finally inside a @contextmanager function whenever cleanup must happen even if the with block raises an exception — without it, an exception in the block skips your teardown code entirely.

editor.html
from contextlib import contextmanager

@contextmanager
def announce(name):
    print(f"Starting {name}")
    yield
    print(f"Finished {name}")

with announce("task"):
    print("Doing work")
localhost:3000

2Practical Example

Here is a real-world application of Context Managers (@contextmanager) showing how it is used in production Python code.

editor.html
from contextlib import contextmanager
import time

@contextmanager
def timer():
    start = time.time()
    try:
        yield
    finally:
        print(f"Took {time.time() - start:.2f}s")

with timer():
    sum(range(1000000))
localhost:3000

3Best Practices

Follow these guidelines when working with Context Managers (@contextmanager):

1. Use @contextmanager for straightforward setup/teardown logic instead of writing a full class with __enter__/__exit__

2. Wrap the yield in try/finally when cleanup needs to run even if the with block's code raises an exception

3. Reach for a full class-based context manager instead when you need more state or behavior than a single generator function can naturally express

⚠️

Tip: Wrap the yield in a try/finally inside a @contextmanager function whenever cleanup must happen even if the with block raises an exception — without it, an exception in the block skips your teardown code entirely.

editor.html
from contextlib import contextmanager

@contextmanager
def announce(name):
    print(f"Starting {name}")
    yield
    print(f"Finished {name}")

with announce("task"):
    print("Doing work")
localhost:3000

Examples

Example 01Basic Usage
from contextlib import contextmanager

@contextmanager
def announce(name):
    print(f"Starting {name}")
    yield
    print(f"Finished {name}")

with announce("task"):
    print("Doing work")
Example 02Advanced Example
from contextlib import contextmanager
import time

@contextmanager
def timer():
    start = time.time()
    try:
        yield
    finally:
        print(f"Took {time.time() - start:.2f}s")

with timer():
    sum(range(1000000))

Best Practices

  • Use @contextmanager for straightforward setup/teardown logic instead of writing a full class with __enter__/__exit__
  • Wrap the yield in try/finally when cleanup needs to run even if the with block's code raises an exception
  • Reach for a full class-based context manager instead when you need more state or behavior than a single generator function can naturally express

Interview Question

In a @contextmanager function, why should the yield statement usually be wrapped in a try/finally block?

Hint: Think about what happens to the code after yield if the with block raises an exception.

If the code inside the with block raises an exception, that exception is actually raised at the point of the yield statement inside the generator function. Without a try/finally around it, that exception would immediately propagate out of the generator, skipping every line written after the yield, meaning your teardown/cleanup code would never run. Wrapping the yield in try/finally guarantees the cleanup code in the finally block still executes before the exception continues propagating, exactly mirroring how a regular try/finally guarantees cleanup elsewhere.

Exercises

MediumPractice using Context Managers (@contextmanager) in a real scenario.
View Solution
from contextlib import contextmanager

@contextmanager
def announce(name):
    print(f"Starting {name}")
    yield
    print(f"Finished {name}")

with announce("task"):
    print("Doing work")

Frequently Asked Questions

In a @contextmanager function, why should the yield statement usually be wrapped in a try/finally block?

If the code inside the with block raises an exception, that exception is actually raised at the point of the yield statement inside the generator function. Without a try/finally around it, that exception would immediately propagate out of the generator, skipping every line written after the yield, meaning your teardown/cleanup code would never run. Wrapping the yield in try/finally guarantees the cleanup code in the finally block still executes before the exception continues propagating, exactly mirroring how a regular try/finally guarantees cleanup elsewhere.

Related Functions

with-statementgenerators-yielddecorators