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:

  • An __enter__ method

    • The __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 with statement code block was executed (basically everything under the with statement).
  • An __exit__ method

    • 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:

  1. __init__ method
  2. __enter__ method
  3. The code in the with statement block
  4. __exit__ method

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 __enter__ and __exit__ methods.



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 'Creating Poems!'


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.

Have the 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'.

Sign up to start coding

By signing up for Codecademy, you agree to Codecademy's Terms of Service & Privacy Policy.
Already have an account?