27.6. contextlib – Утилиты для with-инструкции контекстов¶contextlib – Utilities for with-statement contexts
Исходный код: Lib/contextlib.py
Этот модуль предоставляет утилиты для типичных задач, связанных с оператором with. Дополнительную информацию см. также в разделах Типы менеджеров контекста и Менеджеры контекста оператора with.
Предоставляемые функции:
- @contextlib.contextmanager¶
Эта функция – декоратор, который можно использовать для определения фабричной функции для менеджеров контекста оператора with, без необходимости создавать класс или отдельные методы __enter__() и __exit__().
Простой пример (это не рекомендуется как реальный способ генерации HTML!):
from contextlib import contextmanager @contextmanager def tag(name): print("<%s>" % name) yield print("</%s>" % name) >>> with tag("h1"): ... print("foo") ... <h1> foo </h1>
Декорируемая функция при вызове должна возвращать генератор-итератор. Этот итератор должен передать (yield) ровно одно значение, которое будет связано с целями в предложении as оператора with, если оно есть.
В точке, где генератор выполняет yield, выполняется блок, вложенный в оператор with. Затем генератор возобновляется после выхода из блока. Если в блоке возникает необработанное исключение, оно повторно возбуждается внутри генератора в точке, где произошёл yield. Таким образом, можно использовать конструкцию try...except...finally для перехвата ошибки (если она есть) или для обеспечения выполнения очистки. Если исключение перехватывается лишь для того, чтобы залогировать его или выполнить какое-либо действие (а не подавить его полностью), генератор должен возобновить (reraise) это исключение. В противном случае менеджер контекста-генератор сообщит оператору with, что исключение обработано, и выполнение продолжится с оператора, непосредственно следующего за оператором with.
contextmanager() использует ContextDecorator, поэтому создаваемые им менеджеры контекста можно использовать как в качестве декораторов, так и в операторах with. При использовании в качестве декоратора новый экземпляр генератора неявно создаётся при каждом вызове функции (это позволяет менеджерам контекста, созданным contextmanager(), которые в противном случае были бы «одноразовыми», удовлетворять требованию, что менеджеры контекста должны поддерживать множественные вызовы, чтобы использоваться в качестве декораторов).
Изменено в версии 3.2: Использование ContextDecorator.
- contextlib.closing(thing)¶
Возвращает контекстный менеджер, который закрывает thing после завершения блока. По сути эквивалентно следующему:
from contextlib import contextmanager @contextmanager def closing(thing): try: yield thing finally: thing.close()
И позволяет писать код следующим образом:
from contextlib import closing from urllib.request import urlopen with closing(urlopen('http://www.python.org')) as page: for line in page: print(line)
без необходимости явно закрывать page. Даже если произойдёт ошибка, page.close() будет вызван при выходе из блока with.
- class contextlib.ContextDecorator¶
Базовый класс, который позволяет менеджеру контекста также использоваться в качестве декоратора.
Менеджеры контекста, наследующие от ContextDecorator, должны реализовывать __enter__ и __exit__ как обычно. __exit__ сохраняет свою опциональную обработку исключений даже при использовании в качестве декоратора.
ContextDecorator используется contextmanager(), поэтому эта функциональность доступна автоматически.
Пример ContextDecorator:
from contextlib import ContextDecorator class mycontext(ContextDecorator): def __enter__(self): print('Starting') return self def __exit__(self, *exc): print('Finishing') return False >>> @mycontext() ... def function(): ... print('The bit in the middle') ... >>> function() Starting The bit in the middle Finishing >>> with mycontext(): ... print('The bit in the middle') ... Starting The bit in the middle Finishing
Это изменение – всего лишь синтаксический сахар для любой конструкции следующего вида:
def f(): with cm(): # Выполнить действия
ContextDecorator позволяет вместо этого написать:
@cm() def f(): # Выполнить действия
Это даёт понять, что cm применяется ко всей функции, а не только к её части (и экономия уровня отступа тоже приятна).
Существующие менеджеры контекста, у которых уже есть базовый класс, могут быть расширены путём использования ContextDecorator в качестве примесного класса (mixin class):
from contextlib import ContextDecorator class mycontext(ContextBaseClass, ContextDecorator): def __enter__(self): return self def __exit__(self, *exc): return False
Примечание
Поскольку декорированная функция должна допускать многократный вызов, базовый менеджер контекста должен поддерживать использование в нескольких операторах with. Если это не так, то следует использовать исходную конструкцию с явным оператором with внутри функции.
Новое в версии 3.2.