An Introduction to the With Statement in Python

Here’s a comprehensive introduction to the with statement in Python, including how, and when, to use it.
headshot of author Artturi Jalli
Artturi Jalli
Expert Columnist
June 14, 2022
Updated: June 22, 2022
headshot of author Artturi Jalli
Artturi Jalli
Expert Columnist
June 14, 2022
Updated: June 22, 2022

The with statement in Python helps you with resource management. It ensures you don’t accidentally leave any resources open. 

What Is the With Statement in Python?

In Python, the with statement replaces a try-catch block with a concise shorthand. More importantly, it ensures closing resources right after processing them. A common example of using the with statement is reading or writing to a file. A function or class that supports the with statement is known as a context manager. A context manager allows you to open and close resources right when you want to. For example, the open() function is a context manager. When you call the open() function using the with statement, the file closes automatically after you’ve processed the file.

The with statement is a replacement for commonly used try/finally error-handling statements. A common example of using the with statement is opening a file. To open and write to a file in Python, you can use the with statement as follows:

with open("example.txt", "w") as file:
    file.write("Hello World!")

The with statement automatically closes the file after you’ve completed writing it.

Under the hood, the with statement replaces this kind of try-catch block:

f = open("example.txt", "w")

try:
    f.write("hello world")
finally:
    f.close()

Today we’re going to answer the following questions: 

  • What is the with statement?
  • What are context managers?
  • How do I implement a context manager class and a context manager method?
  • How can I get creative with context managers?

But first things first…

 

How Do I Open Files in Python?

To open and write to a file in Python, you can use a try-catch error handling:

f = open("example.txt", "w")

try:
    f.write("hello world")
finally:
    f.close()

This code does the following:

  • The file opens separately using the open() function.
  • Exception handling is used to write "hello world" into the opened file.
  • The file manually closes in the finally block.

This program opens up a file called example.txt. If there is no such file, the program creates a new file. Then the program writes "hello world" to the file and closes it. 

There is nothing wrong with this approach. But there is a more elegant way to do it using the with statement.

Let’s repeat the above example using the with statement:

with open("example.txt", "w") as file:
    file.write("Hello World!")

This simplifies the code because you can let the with statement take care of closing the file after being used. This is what makes using the with statement a recommended way to open files in Python in general. But what does the with statement actually do? Can you call it on any class or object in Python?

Let’s next discuss the with statement in more detail.

More From Artturi JalliWhat Is the @ Symbol in Python and How Do I Use It?

 

The With Statement and Context Managers in Python

You might think the with statement only works with the open() function when dealing with files. But this is not the case. You can create classes and objects that support the with statement, too.

A class or function that supports the with statement is known as a context manager.

You can implement your context manager if you wish to improve handling resources in your project. For a class to qualify as a context manager, it needs to implement these two methods:

  • __enter__()
  • __exit__()

After implementing these methods, you can use the with statement on the objects of the class.

  • When you call the with statement, the __enter__() method is invoked.
  • When you exit the scope of the with block, the __exit__() function is called.

For example, let’s create a file writer context manager. This class works similar to the open() method:

class FileWriter(object):
    def __init__(self, file_name):
        self.file_name = file_name

    def __enter__(self):
        self.file = open(self.file_name, "w")
        return self.file

    def __exit__(self, exception_type, exception_value, traceback):
        self.file.close()

As a result, this program creates a file called example.txt and writes "hello world" into it, just like the open() function.

Let’s go through how the code works.

  • with FileWriter("example.txt") creates a new FileWriter object and calls __enter__().
  • The __enter__() method initializes the resource you want to use. In this case, it opens a text file. It also has to return the descriptor of the resource, so it returns the opened file.
  • The as file assigns the file to a variable file.
  • Finally, the code you want to run with the acquired resource is placed in the with block after the colon.
  • As soon as this code finishes execution, the __exit__() method is automatically called. In this case, it closes the file.
Python Context Managers and the With Statement

 

Write Your Context Manager Methods in Python

So far we’ve discussed:

  • What is a context manager in Python?
  • How do I write a context manager class?
  • How do I use the with statement on a context manager object?

The context manager you previously wrote is a class, but what if you want to create a context manager method similar to the open() function instead? Simple: Python also supports writing context manager methods.

To turn a method into a context manager, use the contextlib module:

  • Import the contextlib module.
  • Mark a method as a context manager with a contextlib.contextmanager decorator.

As an example, let’s create a custom version of the open() method. This function opens a file, writes text to it and closes it.

from contextlib import contextmanager

@contextmanager
def my_open(name):
    try:
        file = open(name, "w")
        yield file
    finally:
        file.close()

with my_open("example.txt") as file:
    file.write("hello world")

This code works such that:

  • The my_open() function is marked as a context manager using contextmanager decorator.
  • The function opens a file.
  • Then it pauses the execution and hands the opened file over to the caller using yield. (If you used return, the file would close immediately. Learn more about using yield in Python.)
  • When the code inside the with block completes, the my_open() function continues execution and closes the file by running the finally block.

So far, we’ve discussed:

  • What is a context manager in Python?
  • How do I write a context manager class?
  • How do I use the with statement on a context manager object?
  • How do I write a context manager function?

But in the previous examples, you’ve only dealt with files. This is not the only use case for context managers. You can also get creative.

Let’s see an example.

More Software Engineering Tutorials From Built In ExpertsCreate React App and TypeScript — A Quick How-To

 

How to Get Creative With Context Managers

A context manager is a class or method that supports the with statement. In other words, it implements the methods __enter__() and __exit__().

Context managers are flexible; you aren’t restricted to using context managers with files only.

For example, let’s create a bullet point list object that supports any number of indentations.

There are many ways to achieve this. But since we’re talking about context managers, let’s write one for this task.

The goal is to create a program that supports a nested with structure that acts as a nested bulleted list. This would make the code of writing bulleted lists readable:

with BulletedList() as bp:
    bp.item("Dessert")
    with bp:
        bp.item("Apple Pie")
        with bp:
            bp.item("Apples")
            bp.item("Cinamon")
            bp.item("Sugar")
            bp.item("Flour")
            bp.item("Eggs")
    with bp:
        bp.item("Hot Chocolate")
        with bp:
            bp.item("Milk")
            bp.item("Chocolate powder")
            bp.item("Cream")

As a result, we want an output like this:

  - Dessert
    * Apple Pie
      o Apples
      o Cinamon
      o Sugar
      o Flour
      o Eggs
    * Hot Chocolate
      o Milk
      o Chocolate powder
      o Cream

Let’s turn this idea into code.

But Before You Do That, Check Out This. . .Stop Using NumPy’s Global Random Seed

 

Implementation

To write a program that behaves as described, let’s write a context manager.

The context manager takes care of the indentations of the bulleted list elements. It also chooses the right bullet type.

Here is how to put it together in code:

class BulletedList:
    def __init__(self):
        self.indent_level = 0

    def __enter__(self):
        self.indent_level += 1
        return self

    def __exit__(self, exception_type, exception_value, traceback):
        self.indent_level -= 1

    def bullet_type(self):
        bullet_types = ["o ", "- ", "* "]
        return bullet_types[self.indent_level % 3]

    def item(self, text):
        print("  " * self.indent_level + self.bullet_type() + text)

Now you can use this class to create bulleted lists.

Finally, let’s inspect how the code works:

  • The BulletedList is a context manager for creating bulleted lists of multiple indentations.
  • A BulletedList object is initialized with the indentation level of 0.
  • When a BulletedList is used with a with statement, the __enter__() runs under the hood. This increases the indentation level by one.
  • The item() function prints the bulleted list item into a correct indentation level. The bullet type is given by the bullet_type() function.
  • When a with block finishes, the __exit__() function gets called automatically. This reduces the indentation level.

Notice how this example has nothing to do with files. Here the context manager is used to take care of the indentation levels of a bulleted list.

More Python Tutorials4 Python Tools to Simplify Your Life

 

The Takeaway

In Python, the with statement replaces a try-catch block with a concise shorthand. More importantly, it ensures closing resources right after processing them. A common example of using the with statement is reading or writing to a file. A function or class that supports the with statement is known as a context manager. A context manager allows you to open and close resources right when you want to.

For example, the open() function is a context manager. When you call the open() function using the with statement, the file closes automatically after you have processed the file.

You may also write your context manager classes and methods.

  • A context manager class needs to implement __enter__() and __exit__() methods.
  • A context manager function is implemented using the contextlib module’s contextmanager decorator.

Feel free to get creative and have some fun with context managers; you aren’t restricted to only dealing with files.

Happy coding!

Expert Contributors

Built In’s expert contributor network publishes thoughtful, solutions-oriented stories written by innovative tech professionals. It is the tech industry’s definitive destination for sharing compelling, first-person accounts of problem-solving on the road to innovation.

Learn More

Great Companies Need Great People. That's Where We Come In.

Recruit With Us