Python basic knowledge combing-the context manager in Python

Python basic knowledge combing-context manager


1 Introduction

In Python, it is inevitable that we will operate files and disconnect the database. These are very common operations. When operating files, we must ensure that we call after opening the file, and we close()need to close()operate after connecting to the database , otherwise it will easily cause resource leakage. , Ranging from slow system processing, to system crash.

Old rules, let’s start with an example:

for x in range(10000000):
    f =open('test.txt','w')
    f.write('hello')

Obviously, such crazy behavior will cause errors, because we have opened files 10 million times without closing them, taking up too many resources and causing the system to crash.

In order to solve this problem, the context manager (context manager) is referenced in Python. The context manager can help you automatically allocate and release resources. The most classic one is the withstatement. For the above example, we generally use the context manager to file Operational:

for x in range(10000000):
    with open('test.txt','w') as f:
        f.write('hello')

In this way, when the file is opened every time, Python will automatically close the file for us, and the corresponding resources can also be released. Of course, we can also rewrite it as:

f = open('test.txt','w')
try:
    f.write('hello')
finally:
    f.close()

Among them, we can also try/except/finallycatch exceptions, but relatively withspeaking, such code seems very redundant.

2. Implementation of Context Manager

2.1 Class-based context manager

We use an example to clarify the principle of the context manager and its internal implementation. In the following example, we manually implement a context manager FileSystem and manually define its open and close operations.

class FileSystem:
    def __init__(self, name, mode):
        print('调用init方法')
        self.name = name
        self.mode = mode
        self.file = None

    def __enter__(self):  # 定义enter方法
        print('调用enter方法')
        self.file = open(self.name, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):  # 定义exit方法
        print('调用exit方法')
        if self.file:
            self.file.close()


with FileSystem('text.txt', 'w') as f:
    print('准备写入文件')
    f.write('hello world')

# 输出
调用init方法
调用enter方法
准备写入文件
调用exit方法

As can be seen from the context manager we implemented, the calling steps are:

  1. First call the __init__method to initialize the object FileSystem, the file name passed in at this time test.txt, the mode is'w'
  2. The method __enter__is called, the file test.txtis opened in writing mode, and the return FileSystemobject is assigned to the variable f
  3. String 'hello world'is written to file
  4. The method __exit__is called to close the file stream that was opened before

__exit__The parameters in the method exc_type, exc_val, exc_tbrespectively represent exception_type, exception_value, and exception_traceback. When we use the withstatement, if an exception occurs, the exception information will be included in these three variables and passed into the method __exit__().

Below we __exit__()add catching exceptions in.

class Error:
    def __init__(self):
        print('调用__init__')

    def __enter__(self):
        print('调用__enter__ ')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('调用__exit__')
        if exc_type:
            print(f'exception_type:{exc_type}')
            print(f'exception_value:{exc_val}')
            print(f'exception_traceback:{exc_tb}')
        return True


with Error() as obj:
    raise Exception('exception raised').with_traceback(None)

# 输出
调用__init__
调用__enter__ 
调用__exit__
exception_type:<class 'Exception'>
exception_value:exception raised
exception_traceback:<traceback object at 0x7fe0a72c5a00>

2.2 Simple database connection based on context manager

class DBConnectionManager:
    def __init__(self, hostname, port):
        self.hostname = hostname
        self.port = port
        self.connection = None
    
    def __enter__(self):
        self.connection = DBClient(self.hostname, self.port)
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.connection.close()
        

with DBConnectionManager('127.0.0.1', '8080') as dbclient:
    pass

2.3 Generator-based context manager

Class-based context managers are our most common and most commonly used form, but the context manager in Python can be implemented based on classes and generators.

For example, we can use decorators contextlib.contextmanagerto define the generator-based context manager we need, and we can also support the with statement. We can achieve this through examples:

from contextlib import contextmanager

@contextmanager
def file_manager(name, mode):
    try:
        f = open(name, mode)
        yield f
    finally:
        f.close()


with file_manager('test.txt', 'w') as f:
    f.write('hello world')

In this code, it file_manager()is a generator. When we execute the with statement, it will open the execution file and return the file object f. When the with statement is executed, the closing file operation in the finally block will be executed.

When we use the generator-based context manager, we no longer need to define __enter__()methods and __exit__()methods, but we must remember to add@contextmanager

The class-based context manager and the generator-based context manager are functionally identical, but:

  • Class-based context manager is more flexible and suitable for the development of large systems;
  • The generator-based context manager is more convenient and concise, suitable for small and medium-sized program development;

3. Summary

There are two types of context managers: ①class-based context manager ②generator-based context manager

Which context manager to use depends on the scenario. The context manager is usually used with the with statement, which greatly improves the conciseness of the program. The context manager generally includes enter、exitthese two parts. Once an exception is thrown, the type of the exception, the value of the exception, the traceback object of the exception, etc. By passing parameters to the __exit__()function, we can customize related operations to handle exceptions.






For the follow-up update of the blog post, please follow my personal blog: Stardust Blog

Guess you like

Origin blog.csdn.net/u011130655/article/details/113019065