Tips: The previous content is relatively basic, the key knowledge in the second half.

The with keyword is familiar to anyone learning Python.

When manipulating text objects, almost everyone tells us to use with open, which is an example of context management. You must be pretty familiar with it, so I’ll cut the crap.

with open('test.txt') as f:
    print f.readlines()
Copy the code

What is a context manager?

The basic grammar

with EXPR as VAR:
    BLOCK
Copy the code

Let’s get some ideas straight

  1. With open(‘ test.txt ‘) as f:
  2. Context manager: open(‘ test.txt ‘)
  3. F is not a context manager, it should be a resource object.

How do I write a context manager?

To implement such a context management yourself, you need to know the context management protocol.

To put it simply, a class that implements __enter__ and __exit__ methods is an instance of a context manager.

Take this example:

class Resource() :
    def __enter__(self) :
        print('===connect to resource===')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb) :
        print('===close resource connection===')
        
    def operate(self) :
        print('===in operation===')
        
with Resource() as res:
    res.operate()
Copy the code

Let’s do that, by the order in which the logs are printed. You can see the execution process.

===connect to resource===
===in operation===
===close resource connection===
Copy the code

It is clear from this example that when you write code, you can put the connection or retrieval of the resource in __enter__ and the closing of the resource in __exit__.

Why a context manager?

When learning, ask yourself a few more why, develop the thinking of some details, help deepen the understanding of knowledge points.

Why use a context manager?

In my opinion, this has to do with the elegant style Python espouses.

  1. You can manipulate (create/get/release) resources, such as file operations, database connections, in a more elegant way.
  2. Exceptions can be handled in a more elegant way;

The first, which we have already covered above, is the connection of resources.

The second one, however, is ignored by most people. I’m going to focus on that here.

As you know, when handling an exception, you usually use a try… execept.. To capture and process. The downside of this is that there are a lot of exception handlers in the main logic of the code, which can greatly affect our readability.

Better yet, use with to hide exception handling.

Using the above code as an example, we place the 1/0 code under Operate that must throw an exception

class Resource() :
    def __enter__(self) :
        print('===connect to resource===')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) :
        print('===close resource connection===')
        return True

    def operate(self) :
        1/0

with Resource() as res:
    res.operate()
Copy the code

When I run it, I’m surprised to find that I can’t get an error.

This is one of the strengths of context management protocols. Exceptions can be caught in __exit__ and it’s up to you to decide whether to throw them or resolve them here. Returning True in __exit__ (default return False if there is no return) tells the Python interpreter that we have caught the exception and do not need to throw it out.

When writing an __exit__ function, note that it must take these three arguments:

  • Exc_type: indicates the exception type
  • Exc_val: exception value
  • Exc_tb: indicates the information about the abnormal error stack

When the main logic code does not raise an exception, all three arguments will be None.

Understand and use contextlib

In the example above, we wrote a class just to build a context manager. Writing a class would be a bit cumbersome if you wanted to implement a simple function. At this point, we thought, it would be nice if we could just write a function to implement the context manager.

Python has already figured this out. It provides a decorator that allows you to turn the function object into a context manager by implementing the function content according to its code protocol.

We implement a context manager with open according to the Contextlib protocol.

import contextlib

@contextlib.contextmanager
def open_func(file_name) :
    # __enter__ method
    print('open file:', file_name, 'in __enter__')
    file_handler = open(file_name, 'r')
	
    # 【 emphasis 】 : yield to
    yield file_handler

    # __exit__ method
    print('close file:', file_name, 'in __exit__')
    file_handler.close()
    return

with open_func('/Users/MING/mytest.txt') as file_in:
    for line in file_in:
        print(line)
Copy the code

The decorator function must be a generator (with yield), and the code before yield corresponds to the contents of __enter__. The code after yield is equivalent to the contents of __exit__.

The above code only serves the first purpose of the context manager (managing resources) and not the second (handling exceptions).

If you want to handle exceptions, you can do this instead.

import contextlib

@contextlib.contextmanager
def open_func(file_name) :
    # __enter__ method
    print('open file:', file_name, 'in __enter__')
    file_handler = open(file_name, 'r')

    try:
        yield file_handler
    except Exception as exc:
        # deal with exception
        print('the exception was thrown')
    finally:
        print('close file:', file_name, 'in __exit__')
        file_handler.close()

        return

with open_func('/Users/MING/mytest.txt') as file_in:
    for line in file_in:
        1/0
        print(line)
Copy the code

It seems that whenever you talk about context managers, most people talk about the classic example of opening a file.

But there are plenty of examples where context managers can be used in real-world development. Let me give you an example of my own.

In OpenStack, when creating a snapshot for a VM, you need to create a temporary folder to store the local snapshot image. After the local snapshot image is created, upload the image to Glance. Then delete the temporary directory.

The main logic of this code is to create a snapshot, while creating a temporary directory is a prerequisite, and deleting a temporary directory is the last step.

Although the code is scanty, logic is not complicated, but after the “create a temporary directory, use to remove the temporary directory” this function, in many parts of a project needs to be used, if this logic to handle can be written as a tool to function as a context managers, the code reuse rate is improved greatly.

The code looks like this

To sum up, there are three benefits to using a context manager:

  1. Improve code reuse rate;
  2. Improve code elegance;
  3. Improve code readability;

Author: MING – Python programming time Source: www.cnblogs.com/wongbingmin… This site uses “signature 4.0 international” creation and sharing agreement, please indicate the author and source in the obvious position of the article.