All the Ways You Can Use Context Managers in Python (67/100 Days of Python)
In Python, context managers are a useful feature that helps in resource management. They allow you to allocate and release resources automatically, and ensure that your code always executes correctly even if there are errors or exceptions. Two ways to implement context managers in Python are by using the with
statement or as decorators. In this tutorial, we will explore the differences between these two approaches.
Using with statement for Context Managers
The with
statement is the most common way to use context managers in Python. It has a simple syntax, which allows you to allocate and release resources automatically:
with open('file.txt', 'r') as f:
# Do something with the file object
In this example, we use the open
function to create a file object and assign it to the variable f
. The with
statement ensures that the file object is automatically closed when we exit the block.
We can also define our own context managers by creating a class that implements the __enter__
and __exit__
methods:
class MyContextManager:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f'Entering context {self.name}')
return self
def __exit__(self, exc_type, exc_value, traceback):
print(f'Exiting context {self.name}')
with MyContextManager('my_context'):
# Do something in the context
In this example, we define a context manager class MyContextManager
. The __enter__
method is called when we enter the context, and the __exit__
method is called when we exit the context. We can use the with
statement to enter the context and execute our code.
Using Decorators to Use Context Managers
We can also define context managers as classes and use them as decorators. This way they can manage the resources of the functions they are applied to. To do that we need to define the __call__()
method and “pretend” that the class is actually a decorator, along with using with self
to manage the resources of the function that the decorator was applied to:
import functools
class MyContextManager:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f'Entering context {self.name}')
return self
def __exit__(self, exc_type, exc_value, traceback):
print(f'Exiting context {self.name}')
def __call__(self, f):
@functools.wraps(f)
def decorated(*args, **kwds):
with self:
return f(*args, **kwds)
return decorated
@MyContextManager(name='my-awesome-context')
def my_function(a):
print('a:', a)
my_function(10)
The MyContextManager
class defines a context manager with an __init__
method to initialize the name attribute, __enter__
and __exit__
methods to enter and exit the context, respectively, and a __call__
method to make the context manager callable.
The __call__
method is what allows the MyContextManager
class to be used as a decorator. When the my_function
is decorated with @MyContextManager(name='my-awesome-context')
, the __call__
method is called and a decorated function is returned. The decorated function uses the with
statement to enter and exit the context.
When the my_function
is called with an argument a
, the decorated function is executed. The context manager's __enter__
method is called, and the context is entered. The decorated function then prints the value of a
. Finally, the context manager's __exit__
method is called, and the context is exited.
This code will output:
Entering context my-awesome-context
a: 10
Exiting context my-awesome-context
So, this was a small hack we could use classes as decorators and apply them as context managers.
This can be especially useful for logging or monitoring purposes. We can annotate many functions with a simple @MyContextManager
and the whole function will be monitored with a context manager without ever using the explicit with
statement in the function.
What’s next?
- If you found this story valuable, please consider clapping multiple times (this really helps a lot!)
- Hands-on Practice: Free Python Course
- Full series: 100 Days of Python
- Previous topic: Creating Custom Context Managers
- Next topic: Multithreading