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:
They acquire a resource (any type) as part of the constructor (or equivalent method) e.g. File descriptor, sockets, locks etc.
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:
fd = open(filename, "w")
if fd is not None:
fd = open(filename, "r+")
if fd is not None:
Let's rewrite the same code with the ‘with’ statement now:
with open(filename, "w") as fd:
with open(filename, "r+") as fd:
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?
It opened the file and assigned it to the variable we asked it to. This variable is accessible as usual
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
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.
def __init__(self, name):
self.__fd = None
self.__fd = open(self.__file)
def __exit__(self, ctx_type, ctx_value, ctx_tb):
if self.__fd is not None:
with MyFile("test.txt") as f:
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.
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.