You are currently viewing Context Managers And The ‘with’ Statement In Python: A Comprehensive Guide With Examples

Context Managers And The ‘with’ Statement In Python: A Comprehensive Guide With Examples

In this article, we’ll look at context managers and how they can be used with Python’s “with” statements and how to create our own custom context manager.

What Is Context Manager?

Resource management is critical in any programming language, and the use of system resources in programs is common.

Assume we are working on a project where we need to establish a database connection or perform file operations; these operations consume resources that are limited in supply, so they must be released after use; otherwise, issues such as running out of memory or file descriptors, or exceeding the maximum number of connections or network bandwidth can arise.

Context managers come to the rescue in these situations; they are used to prepare resources for use by the program and then free resources when the resources are no longer required, even if exceptions have occurred.

Why Use Context Manager?

As previously discussed, context managers provide a mechanism for the setup and teardown of the resources associated with the program. It improves the readability, conciseness, and maintainability of the code.

Consider the following example, in which we perform a file writing operation without using the with statement.

To begin, we had to write more lines of code in this approach, and we had to manually close the file in the finally block.

Even if an exception occurs, finally block will ensure that the file is closed. However, using the open() function with the with statement reduces the excess code and eliminates the need to manually close the file.

In the preceding code, when the with statement is executed, the open() function’s __enter__ method is called, which returns a file object. The file object is then assigned to the variable file by the as clause, and the content of the sample.txt file is written using the variable file. Finally, when the program exits execution, the __exit__ method is invoked to close the file.

We’ll learn more about __enter__ and __exit__ methods in the upcoming sections.

We can check if the file is actually closed or not.

We received the result True, indicating that the file is automatically closed once the execution exits the with block.

Using with Statement

If you used the with statement, it is likely that you also used the context manager. The with statement is probably most commonly used when opening a file.

Here’s a simple program that opens a text file and reads the content. When the open() function is evaluated after the with statement, context manager is obtained.

The context manager implements two methods called __enter__ and __exit__. The __enter__ method is called at the start to prepare the resource to be used, and the __exit__ method is called at the end to release resources.

Python runs the above code in the following order:

  • The with statement is executed, and the open() function is called.
  • The open() function’s __enter__ method opens the file and returns the file object. The as clause then assigns the file object to the file variable.
  • The inner block of the code content = file.read() gets executed.
  • In the end, the __exit__ method is called to perform the cleanup and closing of the file.

Let’s define and implement both these methods in a Python class and try to understand the execution flow of the program.

Creating Context Manager

The context manager will be created by implementing the __enter__ and __exit__ methods within the class. Any class that has both of these methods can act as a context manager.

Defining a Python class

First, we created a class named Conmanager and defined the __enter__ and __exit__ methods inside the class. Then we created the Conmanager object and assigned it to the variable cn using the as clause. We will get the following output after running the above program.

When the with block is executed, Python orders the execution flow as follows:

  • As we can see from the output, the __enter__ method is called first.
  • The code contained within the with statement is executed.
  • To exit the with statement block, the __exit__ method is called at the end.

We can see in the output that we got None values for the exc_typeexc_val, and exc_tb parameters passed inside the __exit__ method of the class Conmanager.

When an exception occurs while executing the with statement, these parameters take effect.

  • exc_type – displays the type of exception.
  • exc_val – displays the message of the exception.
  • exc_tb – displays the traceback object of the exception.

Consider the following example, which shows how these parameters were used when an exception occurred.

When we run the above code, we get the following result.

Instead of getting None values, we got the AttributeError, as shown in the output above and those three parameters displayed certain values.

  • exc_type displayed the <class 'AttributeError'> value.
  • exc_val displayed the 'str' object has no attribute 'read' message.
  • exc_tb displayed the <traceback object at 0x00000276057D4800> value.

Example

In the following example, we’ve created a context manager class that will reverse a sequence.

We’ve created a class called Reverse and defined the __init__ method, which takes data, the __enter__ method, which operates on the data and returns the reversed version of it, and the __exit__ method, which does nothing.

Then we used the with statement to call the context manager’s object, passing the sequence "Geek" and assigning it to the rev using the as clause before printing it. We will get the following output after running the above code.

The upper code contains a flaw because we did not include any exception-handling code within the __exit__ method. What if we run into an exception?

We changed the code within the with statement and attempted to print the rev.copy(). This will result in an error.

Exception Handling

Let’s include the exception handling code in the __exit__ method.

First, we defined the condition to return the reversed sequence if the exc_type is None, otherwise, return the exception type and message in a nicely formatted manner.

The exception was handled correctly by the __exit__ method, and because we returned True when the error occurs, the program execution continues even after exiting the with statement block and we know because the print statement was executed which is written outside the with block.

Conclusion

Context managers provide a way to manage resources efficiently like by preparing them to use and then releasing them after they are no longer needed. The context managers can be used with Python’s with statement to handle the setup and teardown of resources in the program.

However, we can create our own custom context manager by implementing the enter(setup) logic and exit(teardown) logic within a Python class.

In this article, we’ve learned:

  • What is context manager and why they are used
  • Using context manager with the with statement
  • Implementing context management protocol within a class

🏆Other articles you might be interested in if you liked this one

Understanding the basics of the abstract base class(ABC) in Python.

Implement __getitem__, __setitem__ and __delitem__ in Python class to get, set and delete items.

Generate and manipulate temporary files using tempfile in Python.

Using match-case statements for pattern matching in Python.

Comparing the sort() and sorted() function in Python.

Using super() function to implement attributes and methods of the parent class within the child class.

Using str and repr to change the string representation of the objects in Python.


That’s all for now

KeepCoding✌✌