Объяснение менеджеров with и context в Python

with Это ключевое слово знакомо каждому, кто изучает Python.

При манипулировании текстовыми объектами почти каждый разрешит нам их использовать.Это with openпример управления контекстом. Вы, должно быть, хорошо с ним знакомы, я больше не буду говорить чушь.

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

1. какой контекстный менеджер?

Базовая грамматика

with EXPR as VAR:
    BLOCK

Сначала проясните несколько концепций

1. 上下文表达式:with open('test.txt') as f:
2. 上下文管理器:open('test.txt')
3. f 不是上下文管理器,应该是资源对象。

2. как менеджер контекста?

Чтобы реализовать такое управление контекстом самостоятельно, вы должны сначала знать протокол управления контекстом.

Проще говоря, метод __enter__and реализован в классе, а __exit__экземпляр этого класса является диспетчером контекста.

Например, этот пример:

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()

Выполним его и пройдемся по порядку печати журнала. Вы можете знать процесс выполнения.

===connect to resource===
===in operation===
===close resource connection===

Из этого примера очевидно, что при написании кода вы можете поместить соединение или получение __enter__ресурса и записать закрытие ресурса __exit__.

3. почему диспетчер контекста?

Во время учебы задайте себе еще несколько причин и развивайте обдумывание некоторых деталей, что поможет вам глубже понять точки знания.

Зачем использовать диспетчер контекста?

На мой взгляд, это связано с элегантным стилем, который пропагандирует Python.

  1. Вы можете управлять ресурсами (создавать / получать / освобождать) более элегантным способом, например файловыми операциями и подключениями к базе данных;
  2. Вы можете обрабатывать исключения более элегантным способом;

Первый, мы уже брали в качестве примера подключение ресурса.

Второй тип будет проигнорирован большинством людей. Я сосредоточусь на этом здесь.

Как мы все знаем, обработка исключений обычно используется try...execept..для захвата процесса. Одним из недостатков этого является то, что в основной логике кода будет большое количество агентов обработки исключений, что сильно повлияет на нашу читабельность.

Чуть лучше, можно использовать withдля обработки скрытых исключений.

Тем не менее на основе приведенного выше примера кода, мы будем 1/0это 一定会抛出异常的代码написано operateв

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()

После запуска я был удивлен, обнаружив, что ошибок не было.

Это мощный аспект протокола управления контекстом. Исключения можно __exit__перехватить, и вы можете решить, как с ними обращаться, бросить или разрешить здесь. В __exit__взамен True(не возвращается по умолчанию для возврата False), что эквивалентно сказать интерпретатор Python, мы захватили исключение, вам не нужно выбрасывать.

При написании __exit__функции нужно обращать на нее внимание, она должна иметь три параметра:

  • exc_type: тип исключения
  • exc_val: ненормальное значение
  • exc_tb: информация о стеке аномальных ошибок

Когда основной логический код не сообщает об исключении, все эти три параметра будут равны None.

4. как contextlib?

В приведенном выше примере мы просто написали класс для создания диспетчера контекста. Если вы просто хотите реализовать простую функцию, написать класс будет слишком сложно. В этот раз мы подумали, можно ли написать только одну функцию для реализации диспетчера контекста.

Python давно задумывался об этом. Он предоставляет нам декоратор.Вы можете превратить этот объект функции в диспетчер контекста, если реализуете содержимое функции в соответствии с протоколом ее кода.

Мы следуем протоколу contextlib для реализации диспетчера контекста для открытия файлов (с open).

import contextlib

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

    # 【重点】:yield
    yield file_handler

    # __exit__方法
    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)

В декорированной функции это должен быть генератор (с yield), а код до yield эквивалентен __enter__содержимому внутри. Код после yield эквивалентен __exit__содержимому в.

Приведенный выше код может достичь только первой цели диспетчера контекста (управление ресурсами), но не второй цели (обработка исключений).

Если вы хотите обрабатывать исключения, вы можете изменить его на следующее.

import contextlib

@contextlib.contextmanager
def open_func(file_name):
    # __enter__方法
    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)

Кажется, что пока упоминается диспетчер контекста, большинство людей будет говорить о классическом примере открытия файлов.

Но в реальной разработке есть много примеров контекстных менеджеров, которые можно использовать. Приведу собственный пример.

В OpenStack, когда вы создаете снимок виртуальной машины, вам необходимо создать временную папку для хранения локального образа снимка.После создания образа локального снимка загрузите образ в Glance. Затем удалите этот временный каталог.

Основная логика этого кода 创建快照, и 创建临时目录принадлежит к предпосылке 删除临时目录, является завершающим штрихом.

Несмотря на то, что объем кода невелик, а логика не сложна, 创建临时目录,使用完后再删除临时目录функция " " необходима во многих местах проекта. Если эту логическую обработку можно записать как инструментальную функцию в качестве диспетчера контекста, повторное использование кода Скорость также значительно улучшена.

Код такой

Таким образом, использование диспетчера контекста дает три преимущества:

  1. Повышение скорости повторного использования кода;
  2. Улучшение элегантности кода;
  3. Улучшить читаемость кода;

Я рекомендую мою оригинальную « PyCharm китайского Guide » электронную книгу, которая содержит большое количество (300) иллюстраций . Это хорошо сделано и достойны коллекций каждого инженера Python.

Адрес: http://pycharm.iswbm.com

рекомендация

отblog.csdn.net/weixin_36338224/article/details/108994968
рекомендация