Context Managers and Their Uses in Python

Published on:
December 15, 2022

In this article, MarsDev will present about "with" block or "context managers".

The With statement

1. with() - for read method

2. with() - for write method

3. with() - for append method

4. with() - for read and write both

Context Manager

1. Implementing a Context Manager as a Class

Handling Exception

2. Implementing a Context Manager as a Generator

The With statement 

For this article, assume that you have a file named file1.txt with the following text content inside this text file.

Content inside file1.txt

This is the first sentence.

This is the second sentence.

Now, if you want to print its content you need to write the following code.

Once you open a file then you have to close this file also otherwise there will be inconsistency due to an unclosed file that is not in use but can be modified by some bug. So you should explicitly write obj.close() but when you work with the content manager you don't need to write this obj.close() because the context manager closes the file by default after using it. Hence, the inconsistency can be avoided by the content manager. Let's talk about the context manager.

1. with() - for read method 

Note that “r” is used for read,  “w” is used for write,  “a” is used for append, and  “r+” is used for read and write both. So, we are going to use “r” for read file here.   

The output will be the same as using only "with" and not "obj.close()" because "with" by default has "obj.close()" at the end of its code.

Also note that if your input (or output files - in case of writing) are in separate folders, enter the path ie, open(r"/content/file2.txt", " r "), here, the first "r" is used to ignore n escapes in the path, that is, to ignore \n. Else if the file is name given if files are in the same working directory, ie just open("file1.txt", "r").

2. with() - for write method 

We will use “w” for writing in the file.

Now this write code will erase all previous data and overwrite this given input. Therefore, the write method removes the previous data and overwrites the data given in the file. 

If you check inside file1.txt manually or using code you will get new rewritten data.

Note that you can also create a second new file by naming and entering data.

This code will create another file named file2.txt with the following content inside this file.

Content inside file2.txt

This is the first sentence of file2

This is the second sentence of file2

You can check the content inside this file manually or by the following code.

Now you have 2 files named file1.txt and file2.txt.

3. with() - for append method 

We will use “a” to append content in the file. In this method, you can append data to the end of the file. We will append some data inside file1.txt

You can check the content inside this file manually or by following code.

Therefore, the write method and the append method may also create a separate or second file with the content provided by the code. The only difference between the write and append methods is that the append method will add content to the end of the file whereas the write method will overwrite the content from the beginning of the file. No overwriting by the append method but overwriting by the write method.

4. with() - for read and write both 

We will use “r+” for read and write both.

For read

For write

But this will overwrite from the start.

You can check the content inside this file manually or by following the code.

If you want to avoid overwriting, you need to move to the cursor where you want to write the content. This can be done in 2 ways - first either read() your entire file so that after reading the content the cursor is at the end of the content then write the required content.

You can check the content inside this file manually or by the following code.

In the second method, you can find a method to move the cursor to the required position.

You can check the content inside this file manually or by the following code.

So, write the method that overwrites the content from the start of the file by default, but you can move the position or cursor whenever you want to overwrite that content. However, the append method always adds content to the end of the file.

Now, come to the concept of context manager.

Once you open a file then you have to close this file also otherwise there will be inconsistency due to an unclosed file which is not in use but can be modified by some bug. So you should explicitly write obj.close() but when you work with the content manager you don't need to write this obj.close() because the context manager closes the file by default after using it. Hence, the inconsistency can be avoided by the content manager. Let's talk about the context manager.

To check whether the file is closed or not, we use a data scripter as follows below.

But if you check it before closing the file, then will return false.

But if you check it in the context manager it will always return true because the context manager always closes the file or connection after it is used.

Consider the following code

This above code is same as following

Context Manager 

If you use "with" before, you may have already used a context manager. The context manager takes care of setting up some resources. You can perform these any pair of operations. 

  • Enter and Exit
  • Start and Stop
  • Create and Delete
  • Change and Reset
  • Setup and Teardown
  • Lock and Release
  • Open and Close

Because resources are limited in supply. It should be thrown away after use, this also helps to avoid any inconsistency. We can manage resources using context managers.

Syntax

1. Implementing a Context Manager as a Class 

There are two methods __enter__() and __exit__() in pairs. The __enter__() returns resources that need to be managed and the __exit__() does not return anything but performs the cleanup operations.

The __exit__() method takes three arguments and does not return anything. It is always called at the end.

The "with" statement stores the __exit__() method. __enter__() will be called which opens files and returns "file_obj". Now we can use the file to read, write or do anything else. After this, the __exit__() method will be called by the "with" statement, so the file will be closed by the __exit__() method.

Handling Exception

So, using the context manager, you close the file, but what happens when an exception occurs? As you can see the __exit__() method has three arguments: type, value, and traceback. The __exit__() method determines how to close the file. We ignore these steps taken by the __exit__() method. Lets you try to open a file and using for an undefined function.

These steps are taken by the "with" statement when errors occur. The "with" statement passes those three arguments: type, value, and traceback to the __exit__() method that handles the exception. The __exit__() method returns true if the exception is successfully handled otherwise an exception is raised by the "with" statement. The method returns "None" when no return statement is encountered, so our above example returns "None", which causes the above exceptions as output.

We can handle the exception in __exit__() method in the following below.

Output

Exception has been handled

So we handled the exception because __exit__() always returns true.

Note that there is another way to implement context managers. Given above is the implementation of the context manager as a Class. Now, we will implement context managers using the generator.

2. Implementing a Context Manager as a Generator  

We can implement a context manager using decorators and generators. To do this, Python has a module called the contextlib module. Consider the following example.

Note that the decorator function yields only once. This implementation of context managers is easier than the implementation as a Class. But we need prior knowledge about generators, yield, and decorators. This method does not catch any exceptions that may occur.

These are the steps of this method: Python encounters the yield keyword and then creates a generator instead of a normal function because of the yield function. Now, because of the decorators, the context manager calls function names (i.e., open_file in the above example) as its arguments. The "contextmanager" decorator returns the wrapped generator "GeneratorContextManager" that has been assigned to the "open_file" function. Therefore, we call the "generator context manager" by calling the "open_file" function. Hence following is the newly generated context manager.

Coab link - https://colab.research.google.com/drive/1zHArh-0jEMTuI4m9CSo7tZPC6f7m9uKD?usp=sharing 


Similar Posts