with statement in Python
with statement in Python is used in exception handling to make the code cleaner and much more readable. It simplifies the management of common resources like file streams. Observe the following code example on how the use of
with statement makes code cleaner.
Attention geek! Strengthen your foundations with the Python Programming Foundation Course and learn the basics.
To begin with, your interview preparations Enhance your Data Structures concepts with the Python DS Course. And to begin with your Machine Learning Journey, join the Machine Learning - Basic Level Course
Notice that unlike the first two implementations, there is no need to call
file.close() when using
with statement. The
with statement itself ensures proper acquisition and release of resources. An exception during the
file.write() call in the first implementation can prevent the file from closing properly which may introduce several bugs in the code, i.e. many changes in files do not go into effect until the file is properly closed.
The second approach in the above example takes care of all the exceptions but using the
with statement makes the code compact and much more readable. Thus,
with statement helps avoiding bugs and leaks by ensuring that a resource is properly released when the code using the resource is completely executed. The
with statement is popularly used with file streams, as shown above and with Locks, sockets, subprocesses and telnets etc.
Supporting the “with” statement in user defined objects
There is nothing special in
open() which makes it usable with the
with statement and the same functionality can be provided in user defined objects. Supporting
with statement in your objects will ensure that you never leave any resource open.
with statement in user defined objects you only need to add the methods
__exit__() in the object methods. Consider the following example for further clarification.
Let’s examine the above code. If you notice, what follows the
with keyword is the constructor of
MessageWriter. As soon as the execution enters the context of the
with statement a
MessageWriter object is created and python then calls the
__enter__() method. In this
__enter__() method, initialize the resource you wish to use in the object. This
__enter__() method should always return a descriptor of the acquired resource.
What are resource descriptors?
These are the handles provided by the operating system to access the requested resources. In the following code block,
file is a descriptor of the file stream resource.
MessageWriter example provided above, the
__enter__() method creates a file descriptor and returns it. The name
xfile here is used to refer to the file descriptor returned by the
__enter__() method. The block of code which uses the acquired resource is placed inside the block of the
with statement. As soon as the code inside the
with block is executed, the
__exit__() method is called. All the acquired resources are released in the
__exit__() method. This is how we use the
with statement with user defined objects.
This interface of
__exit__() methods which provides the support of
with statement in user defined objects is called Context Manager.
The contextlib module
A class based context manager as shown above is not the only way to support the
with statement in user defined objects. The
contextlib module provides a few more abstractions built upon the basic context manager interface. Here is how we can rewrite the context manager for the
MessageWriter object using the
In this code example, because of the
yield statement in its definition, the function
open_file() is a generator function.
open_file() function is called, it creates a resource descriptor named
file. This resource descriptor is then passed to the caller and is represented here by the variable
my_file. After the code inside the
with block is executed the program control returns back to the
open_file() function. The
open_file() function resumes its execution and executes the code following the
yield statement. This part of code which appears after the
yield statement releases the acquired resources. The
@contextmanager here is a decorator.
The previous class-based implementation and this generator-based implementation of context managers is internally the same. While the later seems more readable, it requires the knowledge of generators, decorators and