Correctly understand the keyword "with" in python and the context manager

  • 2020-05-30 20:27:46
  • OfStack

preface

If you have a habit of reading the source code, you will probably see some good code with the "with" keyword in it. What situations is it usually used in? Today we'll talk about with and the context manager.

For system resources such as files, database connections, socket, one of the first things your application must do after it has opened these resources and executed the business logic is to close (disconnect) the resource.

For example, the Python program opens a file, writes to the file, and then closes the file, otherwise what will happen? In extreme cases, the "Too many open files" error occurs because the maximum number of files you are allowed to open is limited.

Similarly, for a database, "Can not connect to MySQL server many connections" may occur if too many connections are not closed in time, because a database connection is a very expensive resource and cannot be created indefinitely.

Let's see how to close a file properly.

Normal version:


def m1():
 f = open("output.txt", "w")
 f.write("python zen ")
 f.close()

There is a potential problem with this writing. If an exception occurs during the call to write and the subsequent code cannot continue to execute, the close method cannot be called normally, so the resource will be released by the hoarser of the program. So how do you improve your code?

The advanced version:


def m2():
 f = open("output.txt", "w")
 try:
 f.write("python zen ")
 except IOError:
 print("oops error")
 finally:
 f.close()

The modified version of the program is to capture the code where an exception may occur by try, using the try/finally statement, which means that if an exception occurs in the try code block, the subsequent code will not be executed, but will jump directly to the except code block. In any case, the code for the finally block will eventually be executed. Therefore, as long as you put close in finally code, file 1 is bound to close.

The advanced version:


def m3():
 with open("output.txt", "w") as f:
 f.write("Python zen ")

A more concise and elegant way is to use the with keyword. The return value of the open method is assigned to the variable f, which is automatically called when leaving the with code block f.close() The with function and the try/finally statements are 1 - like. So how does it work?

Another concept that needs to be covered before we get into the mechanics of with is the context manager (Context Manager).

Context manager

Any implementation that __enter__() and __exit__() Method objects can be called context managers, and context manager objects can use the with keyword. Obviously, the file (file) object also implements the context manager.

So how do file objects implement both methods? We can simulate the implementation of one of our own file classes __enter__() and __exit__() Methods.


class File():

 def __init__(self, filename, mode):
 self.filename = filename
 self.mode = mode

 def __enter__(self):
 print("entering")
 self.f = open(self.filename, self.mode)
 return self.f

 def __exit__(self, *args):
 print("will exit")
 self.f.close()

__enter__() Method returns the resource object, which is the file object that you're going to open, __exit__() Method handles some cleanup work.

Because the File class implements the context manager, you can now use the with statement.


with File('out.txt', 'w') as f:
 print("writing")
 f.write('hello, python')

In this way, you do not need to call the close method explicitly, but the system will call it automatically, even if there is an exception in the middle of the close method will be called.

contextlib

Python also provides a decorator for contextmanager, further simplifying the implementation of the context manager. The function is divided into two parts by yield. The statement before yield is __enter__ Method, the statement after yield __exit__ Method. The value immediately following yield is the return value of the function.


from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
 f = open(path, mode)
 yield f
 f.close()

call


with my_open('out.txt', 'w') as f:
 f.write("hello , the simplest context manager")

conclusion


Related articles: