Context Managers

Manage setup and cleanup automatically with the with statement.

Syntaxwith manager() as value: ...

A context manager handles setup and cleanup around a block of code. The with statement you used for files is one — it guarantees the file is closed afterwards.

Writing your own

The simplest way is the contextlib.contextmanager decorator on a generator: code before yield is setup, and code after it is cleanup.

Example

Example · python
from contextlib import contextmanager

@contextmanager
def tag(name):
    print(f'<{name}>')
    yield
    print(f'</{name}>')

with tag('p'):
    print('Hello')

# Output:
# <p>
# Hello
# </p>

When to use it

  • A database connection is opened inside 'with' to guarantee it is closed even if an exception occurs.
  • A temporary directory is created with contextlib.TemporaryDirectory and deleted automatically on exit.
  • A threading lock is acquired with 'with lock:' to ensure it is always released when the block exits.

More examples

File context manager

Uses 'with open()' to guarantee the file is closed when the block exits, error or not.

Example · python
with open('output.txt', 'w') as f:
    f.write('Written safely\n')
# File is automatically closed here, even if an error occurs
print('File closed:', f.closed)   # True

Custom context manager with class

Implements __enter__ and __exit__ to create a reusable context manager that times a block.

Example · python
class Timer:
    import time
    def __enter__(self):
        self.start = __import__('time').perf_counter()
        return self
    def __exit__(self, *args):
        self.elapsed = __import__('time').perf_counter() - self.start

with Timer() as t:
    total = sum(range(1_000_000))
print(f'Elapsed: {t.elapsed:.3f}s')

contextlib.contextmanager

Uses the @contextmanager decorator to write a generator-based context manager without a class.

Example · python
from contextlib import contextmanager

@contextmanager
def managed_resource(name):
    print(f'Acquiring {name}')
    try:
        yield name
    finally:
        print(f'Releasing {name}')

with managed_resource('database connection') as res:
    print(f'Using {res}')
# Acquiring database connection
# Using database connection
# Releasing database connection

Discussion

  • Be the first to comment on this lesson.