Python is a particularly clean and sugary language, thanks to its many convenience features. In this post I will go into context managers in Python, how to use them, where to find them, and how to write your own ones.
Why do we need Context Managers?
Context managers are most often used when we are talking about resources. For example, reading/writing from files. Examine this simple snippet:
f = open('log.txt', 'w') f.write('hello world') f.close()
All this code does is open the
log.txt for writing and writes
hello world in it. Simple enough. But suppose for a minute, that the code on line 2 throws an exception for no reason whatsoever. In this case, file is still opened, but never closed, as the interpreter never gets to line 3. This is not a problem with a throwaway script, but can become a headache if you are to develop anything serious on Python.
How can we fix this? The
finally construction comes into mind:
f = open('log.txt', 'w') try: f.write('hello world') finally: f.close()
This works, since
finally will be executed even if an exception is thrown and file descriptor will be discarded. However, this does not look very Pythonish…
This is where the
with keyword comes into play. It was developed for this specific purpose: to make resource management readable. This snippet is functionally equal to the example above:
with open('log.txt', 'w') as f: f.write('hello world')
Looks much cleaner, won’t you agree?
How do Context Managers work?
The logic behind context managers is actually pretty easy. Let’s explore it by writing our own context manager. For an object to be recognized as a context manager, it must implement 2 methods:
If you try running this example, you will get the following output:
Entered into context manager! Inside context manager! Exiting context manager!
Now let’s try throwing an exception inside
with TestContextManager(): raise Exception()
If you try running this version, note that the
__exit__ method is still called, much like the
finally clause in the
try block. This can be used to clean up and release any resources used by this context manager.
All of this would not be particularly useful if not for the
contextlib module. This module is part of the standard library and provides some common constructs to make your life easier.
Most notable addition is the
@contextmanager decorator. It lets you convert any generator function into a context manager with no additional code at all! Here is an example:
While this still requires the use of
finally, this lets you abstract this away once and forget about it, while also keeping your code compatible with the rest of the codebase which might not understand context managers as well as you now do.
Another useful feature of
contextlib is the
AbstractContextManager class (new in Python 3.6). This is an Abstract Base Class which implements the
__exit__ functions. Use it in your custom context managers to make sure the types are compatible.
Thank you for reading, I hope you enjoyed this article. In my next post, I will talk about Asynchronous Context Managers. Stay tuned!