The contextlib module of Python’s standard library provides utilities for resource allocation to the with
statement.
The with
statement in Python is used for resource management and exception handling. Therefore, it serves as a good with
statement, it allocates resources temporarily. As soon as the with
statement block finishes, all the resources initially consumed by with
are released.
There are three functions provided by the contextlib module:
A Context Manager requires two basic functions: __enter__()
and __exit__()
. You can implement a context manager in a class by writing __enter__()
and __exit__()
functions. By defining these functions in a class, we can use it alongside the with
statement to provide optimal resource allocation. Take a look at the following code:
class myFileHandler():def __init__(self, fname, method):print("I'm in contructor")self.file_obj = open(fname, method)def __enter__(self):print("I'm in ENTER block")return self.file_objdef __exit__(self, type, value, traceback):print("I'm in EXIT block")self.file_obj.close()with myFileHandler("this_file.txt", "w") as example:######### read write statements #########print("I'm in WITH block")pass
The with statement calls our class myFileHandler()
. The __enter__()
method calls the constructor, opens a file using the parameters passed, and returns the file object to the with
statement, which then assigns it to example
. Then, the statements in the with
block are executed. As soon as it exits the with
block, the __exit__()
method is invoked, and the process is gracefully terminated.
The entire process above can be replicated using the contextmanager(func)
utility with the need for separate __enter__()
and __exit__()
methods. Take a look at the following code:
from contextlib import contextmanager@contextmanagerdef thisFunc(fname, method):print("This is the implicit ENTER block")my_file = open(fname, method)yield my_fileprint("This is the implicit EXIT block")my_file.close()with thisFunc("this_file.txt", "w") as example:######### read write statements #########print("I'm in WITH block")pass
It is fair to say that using the contextmanager(func)
(as a decorator) makes the code more readable and easier to understand. The decorated contextmanager(func)
has three sections:
yield
statement does what an __enter__()
method does.with
statements as
clause - example
. At this stage, the control is passed to the with
statement.yield
statement does what an __exit__()
method does.The contextlib module provides us with a utility to use when we need to call multiple functions with the with
statement. The nested()
function makes our lives easier in this scenario. Consider the following nested with
statements:
with X as file_1:
with Y as file_2:
with Z as file_3:
# statements for file_1, file_2, and file_3
The nested function contracts the code above into one line
from contextlib import nested
with nested(X, Y, Z) as (file_1, file_2, file_3):
# statements for file_1, file_2, and file_3
If an __exit__()
method of a Context Manager (X, Y, or Z) suppresses an exception, then the exception will not be passed to all the outer Context Managers. Similarly, If an __exit__()
method of a Context Manager (X, Y, or Z) raises an exception, then the exception will be passed to all the outer Context Managers.
The closing(thing)
method returns a Context Manager that closes thing
upon completion of the with
block. For example:
from contextlib import contextmanager
import socket
@contextmanager
def closing(thing):
try:
thing.connect("localhost", 8333)
yield thing
finally:
thing.close()
sock = socket.socket()
with closing(sock) as my_sock:
my_sock.listen()
The code above sends a socket object to the decorated Context Manager closing(thing)
. The Context Manager establishes a connection and returns the thing
object back to the with
statement. When the with
statement block commences, the socket object is closed.
The closing(thing)
function of contextlib module lets you write the code above like this:
from contextlib import closing
import socket
with closing(socket.socket()) as my_sock:
my_sock.listen()
Free Resources