All the Ways You Can Use Context Managers in Python (67/100 Days of Python)

Martin Mirakyan
3 min readMar 9, 2023
Day 67 of the “100 Days of Python” blog post series covering different ways of using context managers

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?

--

--

Martin Mirakyan

Software Engineer | Machine Learning | Founder of Profound Academy (https://profound.academy)