What is context manager in Python?
Python provides a statement that enables the developer to execute the concept of the
This can be broken up and learned using the analogy of the local variable inside the function. The local variable is created when a function is called automatically, and is ideally deleted when a function has completed execution.
In a case like this, we are releasing memory when we’re done with it. Similarly, in the case of context managers, the resource is locked or marked as ‘in use’ when the interpreter enters the context manager block. As soon as it’s exited, the lock is released.
Managing resources
In any programming language, the
If these resources are not released, they will lead to resource leakage and cause the system to either crash or slow down, which will be instrumental if a user has a teardown of resources and automatic setup. In Python, this can be realized with context managers, which facilitate the proper handling of resources.
Managing resources using a context manager
If a block of code lifts an exception or has a complex algorithm with multiple return paths, it will become cumbersome to close the file in all the available places.
Generally, in different languages, when working with files, try-except-finally is used to confirm that the file resource is closed after usage even if there is an exception. However, Python gives an easy way to manage resources with context managers.
Context managers can be written using classes or functions (with decorators). When it gets evaluated, it should result in an object that performs context management.
Creating a context manager
When we create context managers using classes, users need to ensure that the class has the _enter() and __exit() methods.
The __enter() method returns the resource that needs to be managed; while, the __exit_() method does not return anything and performs the clean-up operations.
In this case, a ContextManager object is created. This object is assigned to the variable after the
_init_()_enter_()- statement body (code inside the
withblock) _exit_()(the parameters in this method are used to manage exceptions)
File management using a a context manager
Let’s apply the above concept to create a class that helps with file resource management. The File Manager class helps with opening a file, writing/reading contents, and then closing it.
Implementing a context manager as a class
Our _exit_ method accepts three arguments. They are useful by every _exit_ method, a part of the ContextManager class. Let’s talk about what will happen under the hood:
- The
withstatement will store the_exit_method of a File class. - It will call the
_enter_method of a File class. - The
_enter_method will open the file and return it. - The opened file handle is passed to
opened_file. - We write to a file using
.write(). - The
withstatement calls the stored_exit_method. - The
_exit_method closes the file.
Handling exceptions
We did not discuss the type, value, or traceback arguments of an _exit_ method. If an exception occurs between the 4th and the 6th step, Python passes a type, value, and traceback of an exception to an _exit_ method. It allows an _exit_ method to decide how to close a file, and if any further steps are needed.
Database connection management using context manager
Let’s create a simple connection management system for a database. The number of database connections that can be opened at one time is also limited (just like the file descriptors). Therefore, the context managers are helpful in managing connections to the database, as there may be a chance that the programmer will forget to close the connection.
Code
Basic context manager
class ContextManager():def __init__(self):print('init method called')def __enter__(self):print('enter method called')return selfdef __exit__(self, exc_type, exc_value, exc_traceback):print('exit method called')with ContextManager() as manager:print('with statement block')
Loading a file
class FileManager():def __init__(self, filename, mode):self.filename = filenameself.mode = modeself.file = Nonedef __enter__(self):self.file = open(self.filename, self.mode)return self.filedef __exit__(self, exc_type, exc_value, exc_traceback):self.file.close()# loading a filewith FileManager('test.txt', 'w') as f:f.write('Test')print(f.closed)
Handling exception
class File(object):def __init__(self, file_name, method):self.file_obj = open(file_name, method)def __enter__(self):return self.file_objdef __exit__(self, type, value, traceback):print("Exception has been handled")self.file_obj.close()return Truewith File('demo.txt', 'w') as opened_file:opened_file.undefined_function()