import contextlib @contextlib.contextmanager
with
.Функция contextmanager()
модуля contextlib
представляет собой декоратор, который можно использовать для определения фабричной функции для оператора контекстных менеджеров with
без необходимости создавать класс или отдельные методы __enter__()
и __exit__()
.
Хотя многие объекты изначально поддерживают использование в операторах with
, иногда необходимо управлять ресурсом, который сам по себе не является диспетчером контекста и не реализует метод close()
для использования с функцией contextlib.closing()
.
Абстрактный пример для обеспечения правильного управления ресурсами:
from contextlib import contextmanager @contextmanager def managed_resource(*args, **kwds): # Код для выделения ресурса, например: resource = acquire_resource(*args, **kwds) try: yield resource finally: # Код для освобождения ресурса, например: release_resource(resource) >>> with managed_resource(timeout=3600) as resource: ... # Ресурс освобождается в конце этого блока, ... # Даже если код в блоке вызывает исключение
Декорируемая функция, при вызове, должна возвращать генератор-итератор. Этот генератор-итератор должен выдавать ровно одно значение, которое будет привязано к целям в предложении with
оператора as
, если оно есть.
В тот момент, когда генератор дает результат, выполняется блок, вложенный в оператор with
. Затем генератор возобновляется после выхода из блока. Если в блоке возникает необработанное исключение, оно повторно вызывается внутри генератора в точке, где произошел выход. Таким образом, можно использовать оператор try…except
…finally
, чтобы перехватить ошибку, если она есть или убедиться, что произошла какая-то очистка. Если исключение перехвачено просто для того, чтобы зарегистрировать его или выполнить какое-либо действие, а не полностью его подавить, то генератор должен повторно вызвать это исключение. В противном случае менеджер контекста генератора укажет оператору with
, что исключение обработано, и выполнение возобновится с оператором, следующим сразу за оператором with
.
Функция contextlib.contextmanager()
использует класс contextlib.ContextDecorator()
, поэтому создаваемые им менеджеры контекста могут использоваться как в качестве декораторов, так и в операторах with
.
При использовании в качестве декоратора новый экземпляр генератора неявно создается при каждом вызове функции. Это позволяет другим одноразовым менеджерам контекста, созданным с использованием декоратора @contextmanager
, выполнить требование, чтобы менеджеры контекста поддерживали множественные вызовы.