Now that we have an understanding of why we need context managers and the power of the
with statement, it is essential for us to know what’s happening under the hood to gain a much deeper understanding of the concept. The best way to see the internal workings of a context manager (such as the
with statement) is by creating our own!
One of the two approaches of creating context managers is referred to as the class-based approach. The class-based approach of writing context managers requires explicitly defining and implementing the following two methods inside of a class:
__enter__method allows for the setup of context managers. This method commonly takes care of opening resources (like files). This method also begins what is known as the runtime context - the period of time in which a script runs. In our previous examples, it was the time in which the code passed into the
withstatement code block was executed (basically everything under the
__exit__ensures the breakdown of the context manager. This method commonly takes care of closing open resources that are no longer in use.
To visualize these methods and the approach, let’s take a look at a custom class-based context manager below:
class ContextManager: def __init__(self): print('Initializing class...') def __enter__(self): print('Entering context...') def __exit__(self, *exc): print('Exiting context...')
Here, we defined a new class called
ContextManager (to be extra explicit) and implemented the required methods. By defining these two methods, we are implementing the context management protocol - a guideline for the required methods for a context manager. Don’t get too caught up in the arguments passed to each method, we will talk through them in the next exercises, but they are required to not experience an error.
Implementing the context management protocol allows us to immediately invoke the class using the
with statement as shown below:
with ContextManager() as cm: print('Code inside with statement')
Here we invoke the
ContextManager class with a
with statement. Following that the
with statement, our context manager prints
'with statement block'.
After running the code, our output of this context manager would be:
Initializing class... Entering context... Code inside with statement Exiting context...
The above shows that our context manager class is executed in the following sequence:
- The code in the
Let’s practice getting down the basics of writing a class-based context manager in addition to the execution flow before diving deeper into the
Let’s create a context manager that will work with files filled with creative poems. While we won’t directly work with a file in this exercise, make sure to note the order of method execution in a context manager. Don’t worry, we’ll work with an actual file soon! For now, we are just going to get comfortable with the basics.
Create a class called
PoemFiles. For now, give it a single
pass statement so it won’t create an error when run.
Next, remove the
pass statement and create an
__init__ method inside of the
PoemFiles class that prints
Let’s implement the
__enter__ method. Have the method print
'Opening poem file'.
Lastly, create an
__exit__ method that prints
'Closing poem file'.
Awesome! Now we have our very own context manager! Let’s see it in action by calling it with a
with statement save the invoked class to a variable called
manager and have it print a famous line from the poet Emily Dickinson:
'Hope is the thing with feathers'.