Перейти к содержанию

Менеджер контекста

Определение

Менеджер контекста позволяет выделять и освобождать ресурсы строго по необходимости. В Python за это отвечает блок с оператором with. Он имеет следующую конструкцию:

[async] with <функция> as <переменная>:
    ...

Протокол контекстного менеджера

Контекстный менеджер должен иметь следующие методы:

  1. Если он синхронный - __enter__ и __exit__
  2. Если асинхронный - __aenter__ и __aexit__

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

Обработка исключений

В случае возникновения исключения в метод __exit__ передается 3 параметра - type, value и traceback.

При обработке исключений важно то, что возвращает метод __exit__. Тут есть 2 варианта:

  1. В случае, если исключение было обработано нормально метод должен вернуть True.
  2. Возращение любого другого значения возбудит исключение, которое передадится на уровень выше.

Контекстный менеджер из генератора

Здесь на помощь к нам приходит contextlib. С помощью contextmanager мы можем реализовать менеджер контекста через декоратор и генераторы. Выглядит это так:

from contextlib import contextmanager

@contextmanager
def open_file(name):
    f = open(name, 'w')
    yield f
    f.close()

Работает это следующим образом:

  1. Python встречает ключевое слово yield, из-за чего создается генератор, а не функция. Генератор должен возвращать только одно значение, оно будет привязано к with.
  2. Тот в свою очередь реализует методы __enter__ и __exit__. Всё что до использования генератора попадает в __enter__, а после него - в __exit__
  3. В случае возникновения исключения оно вызывается внутри генератора, поэтому можно его словить и обработать при помощи конструкции `try ... except ...

Для асинхронных функций нужно использовать asynccontextmanager из того же contextlib