28.7. contextlib – Утилиты для контекстов оператора with¶contextlib – Utilities for with-statement contexts
Новое в версии 2.5.
Исходный код: Lib/contextlib.py
Этот модуль предоставляет утилиты для типовых задач, связанных с оператором with.
Для получения дополнительной информации см. также Типы менеджеров контекста и
Менеджеры контекста оператора with.
Предоставляемые функции:
-
contextlib.contextmanager(func)¶ Эта функция – декоратор, который можно использовать для определения фабричной функции для контекстных менеджеров оператора
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: ... # Ресурс освобождается в конце этого блока, ... # даже если код в блоке вызывает исключение
Декорируемая функция при вызове должна возвращать генератор-итератор. Этот итератор должен возвращать ровно одно значение, которое будет связано с целями в предложении
asоператораwith, если таковые имеются.В момент, где генератор делает yield, выполняется блок, вложенный в оператор
with. Затем генератор возобновляется после выхода из блока. Если в блоке возникает необработанное исключение, оно повторно возбуждается внутри генератора в точке, где произошел yield. Таким образом, можно использовать операторtry…except…finallyдля перехвата ошибки (если есть) или для выполнения очистки. Если исключение перехватывается только для того, чтобы зарегистрировать его или выполнить какое-либо действие (а не подавить полностью), генератор должен повторно возбудить это исключение. В противном случае контекстный менеджер генератора сообщит операторуwith, что исключение обработано, и выполнение продолжится с оператора, следующего сразу за операторомwith.
-
contextlib.nested(mgr1[, mgr2[, ...]])¶ Объединяет несколько менеджеров контекста в один вложенный менеджер контекста.
Эта функция устарела в пользу формы с несколькими менеджерами оператора
with.Единственное преимущество этой функции перед формой с несколькими менеджерами оператора
withзаключается в том, что распаковка аргументов позволяет использовать её с переменным числом менеджеров контекста следующим образом:from contextlib import nested with nested(*managers): do_something()
Обратите внимание: если метод
__exit__()одного из вложенных менеджеров контекста указывает, что исключение должно быть подавлено, информация об исключении не будет передана ни одному из оставшихся внешних менеджеров контекста. Аналогично, если метод__exit__()одного из вложенных менеджеров вызывает исключение, любое предыдущее состояние исключения будет потеряно; новое исключение будет передано методам__exit__()оставшихся внешних менеджеров контекста. В общем случае методы__exit__()должны избегать возбуждения исключений, и в особенности они не должны повторно возбуждать переданное исключение.У этой функции есть два существенных недостатка, из-за которых она была объявлена устаревшей. Во-первых, поскольку все менеджеры контекста создаются до вызова функции, методы
__new__()и__init__()внутренних менеджеров контекста на самом деле не находятся в области действия внешних менеджеров контекста. Это означает, например, что использованиеnested()для открытия двух файлов является ошибкой программирования, так как первый файл не будет своевременно закрыт, если при открытии второго возникнет исключение.Во-вторых, если метод
__enter__()одного из внутренних менеджеров контекста вызывает исключение, которое перехватывается и подавляется методом__exit__()одного из внешних менеджеров контекста, то данная конструкция возбудитRuntimeErrorвместо того, чтобы пропустить тело оператораwith.Разработчикам, которым требуется поддержка вложения переменного числа менеджеров контекста, можно либо использовать модуль
warningsдля подавления DeprecationWarning, возбуждаемого этой функцией, либо взять эту функцию за образец для реализации в своём приложении.Устарело с версии 2.7: Оператор with теперь напрямую поддерживает эту функциональность (без сбивающих с толку чреватых ошибками странностей).
-
contextlib.closing(thing)¶ Возвращает контекстный менеджер, который закрывает thing после завершения блока. По сути эквивалентно следующему:
from contextlib import contextmanager @contextmanager def closing(thing): try: yield thing finally: thing.close()
И позволяет писать код следующим образом:
from contextlib import closing import urllib with closing(urllib.urlopen('http://www.python.org')) as page: for line in page: print line
без необходимости явно закрывать
page. Даже если произойдет ошибка,page.close()будет вызван при выходе из блокаwith.