> **Источник:** https://python-all.ru/2.7/library/contextlib.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# 28.7. [`contextlib`](https://python-all.ru/2.7/library/contextlib.html#module-contextlib) – Утилиты для контекстов оператора [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with)

Новое в версии 2.5.

**Исходный код:** [Lib/contextlib.py](https://python-all.ru/src/2.7/Lib/contextlib.py)

---

Этот модуль предоставляет утилиты для типовых задач, связанных с оператором [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with). Для получения дополнительной информации см. также [Типы менеджеров контекста](https://python-all.ru/2.7/library/stdtypes.html#typecontextmanager) и [Менеджеры контекста оператора with](https://python-all.ru/2.7/reference/datamodel.html#context-managers).

Предоставляемые функции:

#### `contextlib.contextmanager(func)`

Эта функция – [декоратор](https://python-all.ru/2.7/glossary.html#term-decorator), который можно использовать для определения фабричной функции для контекстных менеджеров оператора [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with), без необходимости создавать класс или отдельные методы [`__enter__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__enter__) и [`__exit__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__exit__).

Хотя многие объекты изначально поддерживают использование в with-выражениях, иногда требуется управлять ресурсом, который сам по себе не является менеджером контекста, и не реализует метод `close()` для использования с `contextlib.closing`

Абстрактным примером может служить следующий код для обеспечения корректного управления ресурсами:

```python
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:
...     # Ресурс освобождается в конце этого блока,
...     # даже если код в блоке вызывает исключение
```

Декорируемая функция при вызове должна возвращать [генератор](https://python-all.ru/2.7/glossary.html#term-generator)-итератор. Этот итератор должен возвращать ровно одно значение, которое будет связано с целями в предложении [`as`](https://python-all.ru/2.7/reference/compound_stmts.html#as) оператора [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with), если таковые имеются.

В момент, где генератор делает yield, выполняется блок, вложенный в оператор [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with). Затем генератор возобновляется после выхода из блока. Если в блоке возникает необработанное исключение, оно повторно возбуждается внутри генератора в точке, где произошел yield. Таким образом, можно использовать оператор [`try`](https://python-all.ru/2.7/reference/compound_stmts.html#try)…[`except`](https://python-all.ru/2.7/reference/compound_stmts.html#except)…[`finally`](https://python-all.ru/2.7/reference/compound_stmts.html#finally) для перехвата ошибки (если есть) или для выполнения очистки. Если исключение перехватывается только для того, чтобы зарегистрировать его или выполнить какое-либо действие (а не подавить полностью), генератор должен повторно возбудить это исключение. В противном случае контекстный менеджер генератора сообщит оператору [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with), что исключение обработано, и выполнение продолжится с оператора, следующего сразу за оператором [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with).

#### `contextlib.nested(mgr1[, mgr2[, ...]])`

Объединяет несколько менеджеров контекста в один вложенный менеджер контекста.

Эта функция устарела в пользу формы с несколькими менеджерами оператора [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with).

Единственное преимущество этой функции перед формой с несколькими менеджерами оператора [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with) заключается в том, что распаковка аргументов позволяет использовать её с переменным числом менеджеров контекста следующим образом:

```python
from contextlib import nested

with nested(*managers):
    do_something()
```

Обратите внимание: если метод [`__exit__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__exit__) одного из вложенных менеджеров контекста указывает, что исключение должно быть подавлено, информация об исключении не будет передана ни одному из оставшихся внешних менеджеров контекста. Аналогично, если метод [`__exit__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__exit__) одного из вложенных менеджеров вызывает исключение, любое предыдущее состояние исключения будет потеряно; новое исключение будет передано методам [`__exit__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__exit__) оставшихся внешних менеджеров контекста. В общем случае методы [`__exit__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__exit__) должны избегать возбуждения исключений, и в особенности они не должны повторно возбуждать переданное исключение.

У этой функции есть два существенных недостатка, из-за которых она была объявлена устаревшей. Во-первых, поскольку все менеджеры контекста создаются до вызова функции, методы [`__new__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__new__) и [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__) внутренних менеджеров контекста на самом деле не находятся в области действия внешних менеджеров контекста. Это означает, например, что использование [`nested()`](https://python-all.ru/2.7/library/contextlib.html#contextlib.nested) для открытия двух файлов является ошибкой программирования, так как первый файл не будет своевременно закрыт, если при открытии второго возникнет исключение.

Во-вторых, если метод [`__enter__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__enter__) одного из внутренних менеджеров контекста вызывает исключение, которое перехватывается и подавляется методом [`__exit__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__exit__) одного из внешних менеджеров контекста, то данная конструкция возбудит [`RuntimeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.RuntimeError) вместо того, чтобы пропустить тело оператора [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with).

Разработчикам, которым требуется поддержка вложения переменного числа менеджеров контекста, можно либо использовать модуль [`warnings`](https://python-all.ru/2.7/library/warnings.html#module-warnings) для подавления DeprecationWarning, возбуждаемого этой функцией, либо взять эту функцию за образец для реализации в своём приложении.

Устарело с версии 2.7: Оператор with теперь напрямую поддерживает эту функциональность (без сбивающих с толку чреватых ошибками странностей).

#### `contextlib.closing(thing)`

Возвращает контекстный менеджер, который закрывает *thing* после завершения блока. По сути эквивалентно следующему:

```python
from contextlib import contextmanager

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()
```

И позволяет писать код следующим образом:

```python
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`](https://python-all.ru/2.7/reference/compound_stmts.html#with).

> **См. также**
>
> **[**PEP 343**](https://python-all.ru/2.7/library/contextlib.html) – Оператор «with»**
>
> Спецификация, предыстория и примеры для инструкции [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with) в Python.
