Context managers in Python provide a convenient way to manage resources, such as files or database connections, ensuring they are properly initialized and cleaned up. They are used extensively with the with
statement and are implemented using special methods __enter__()
and __exit__()
.
A context manager is any object that implements the context management protocol:
__enter__()
: Prepares the context and returns the resource to be managed. It is called when entering the with
block.__exit__(exc_type, exc_val, exc_tb)
: Cleans up resources after the with
block exits, even if an exception occurs. It is called upon exiting the with
block.class FileContextManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
# Usage:
filename = 'example.txt'
with FileContextManager(filename, 'w') as file:
file.write('Hello, context managers!')
Context managers can be nested within each other to manage multiple resources simultaneously. This is useful when dealing with dependencies or complex setups.
class NestedContextManager:
def __enter__(self):
print("Entering outer context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting outer context")
class InnerContextManager:
def __enter__(self):
print("Entering inner context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting inner context")
# Usage:
with NestedContextManager():
with InnerContextManager():
print("Inside inner context")
print("Outside inner context")
print("Outside outer context")
The contextlib
module provides utilities for creating and working with context managers. It includes decorators (@contextmanager
) and helper functions (closing()
, redirect_stdout()
, etc.) to simplify context manager creation.
Example using contextlib.contextmanager
:
from contextlib import contextmanager
@contextmanager
def timer(label):
import time
start = time.time()
try:
yield
finally:
end = time.time()
print(f"{label}: {end - start} seconds")
# Usage:
with timer("Execution time"):
# Code block to measure time
import time
time.sleep(2)
For managing asynchronous resources or operations, Python introduced async context managers, which use async def
and async with
syntax.
class AsyncResource:
async def __aenter__(self):
# Async setup code
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
# Async cleanup code
pass
# Usage:
async def use_async_resource():
async with AsyncResource() as resource:
# Async code using the resource
# Call async function
await use_async_resource()