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?
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.
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 newFileWriter
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 variablefile
. - 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.
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 usingcontextmanager
decorator. - The function opens a file.
- Then it pauses the execution and hands the opened file over to the caller using
yield
. (If you usedreturn
, the file would close immediately. Learn more about using yield in Python.) - When the code inside the
with
block completes, themy_open()
function continues execution and closes the file by running thefinally
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.
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.
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 of0
. - When a
BulletedList
is used with awith
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 thebullet_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.
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’scontextmanager
decorator.
Feel free to get creative and have some fun with context managers; you aren’t restricted to only dealing with files.
Happy coding!