Multipurpose 'with' statement in Python

Updated: Jun 11, 2018


Is it possible to create a scoped lock, or as well known in the C++ world - Resource-Acquisition-is-Initialization (RAII) classes in python? The answer is resounding yes! But before we get there, let's step back and find what RAII classes do:

  1. They acquire a resource (any type) as part of the constructor (or equivalent method) e.g. File descriptor, sockets, locks etc.

  2. They release or close resource as part of the destructor.

Now this sounds simple but it is very powerful technique. This technique is the basis of scoped locks or smart pointers in C++ like languages. In case of Python, it provides something more on top of this and that best thing is ‘with’ statement. Let's look at an example.


Let's say we want to open a file, write to it and then close the file. Normally you would do it like this:


def WriteFile(filename):

fd = open(filename, "w")

if fd is not None:

fd.write("Hello World")

fd.close()


def ReadFile(filename):

fd = open(filename, "r+")

if fd is not None:

print(fd.read())

fd.close()


WriteFile("test.txt")

ReadFile("test.txt")


Result:

Hello World


Let's rewrite the same code with the ‘with’ statement now:


def WriteFilev2(filename):

with open(filename, "w") as fd:

fd.write("Hello World")


def ReadFilev2(filename):

with open(filename, "r+") as fd:

print(fd.read())


WriteFilev2("test.txt")

ReadFilev2("test.txt")


Result:

Hello World


Notice how it changed to two-liner block from four lines earlier? Sweet, isn’t it? So what did ‘with’ statement did for us here?

  1. It opened the file and assigned it to the variable we asked it to. This variable is accessible as usual

  2. It created a new scope for file variable within which that var works as if we manually opened the file and assigned it to the variable

  3. As soon as new scope ended, it closed file for us (fd.close() was called for us)


This is a pretty useful technique to create scoped locks, scoped resources etc. Most, if not all, built in resource classes honor protocol or interface needed by with statement. So that means we should be able to use ‘with’ with any resource classes in python (file, sockets, locks etc.), pretty powerful!


So how do we extend this interface to our own classes? It’s pretty simple. To implement ‘with’ interface for the class, you need to implement two special dunder methods in your class – ‘enter’ and ‘exit’. To demonstrate those, let's create a special class for File reads.


class MyFile:

def __init__(self, name):

self.__file= name

self.__fd = None


def __enter__(self):

print("Opening file")

self.__fd = open(self.__file)

return self.__fd


def __exit__(self, ctx_type, ctx_value, ctx_tb):

if self.__fd is not None:

print("Closing file")

self.__fd.close()


with MyFile("test.txt") as f:

print("File contents:")

print(f.read())


Result:

File contents:

Hello World

Closing file


See the beauty of it? This really allows us to extend this to any and all resource classes you may define. If you are writing common code or any code in the reusable library, you will make the life of your co-workers or your library users much easier with this.

Summary:

Python allows us to create a scope for resource objects with ‘with’ statement. It simplifies code and makes it easier to maintain. It also makes sure resource get released at right point in time, as soon as scope ends for the resource. It allows to implement RAII classes for any resources you may ever use in your program.