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

---

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

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

---

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

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

#### `@contextlib.contextmanager`

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

Простой пример (это не рекомендуется как реальный способ генерации HTML!):

```python
from contextlib import contextmanager

@contextmanager
def tag(name):
    print("<%s>" % name)
    yield
    print("</%s>" % name)

>>> with tag("h1"):
...    print("foo")
...
<h1>
foo
</h1>
```

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

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

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

Изменено в версии 3.2: Использование [`ContextDecorator`](https://python-all.ru/3.2/library/contextlib.html#contextlib.ContextDecorator).

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

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

```python
from contextlib import contextmanager

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

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

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

#### `class contextlib.ContextDecorator`

Базовый класс, который позволяет менеджеру контекста также использоваться в качестве декоратора.

Менеджеры контекста, наследующие от `ContextDecorator`, должны реализовывать `__enter__` и `__exit__` как обычно. `__exit__` сохраняет свою опциональную обработку исключений даже при использовании в качестве декоратора.

`ContextDecorator` используется [`contextmanager()`](https://python-all.ru/3.2/library/contextlib.html#contextlib.contextmanager), поэтому эта функциональность доступна автоматически.

Пример `ContextDecorator`:

```python
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
```

Это изменение – всего лишь синтаксический сахар для любой конструкции следующего вида:

```python
def f():
    with cm():
        # Выполнить действия
```

`ContextDecorator` позволяет вместо этого написать:

```python
@cm()
def f():
    # Выполнить действия
```

Это даёт понять, что `cm` применяется ко всей функции, а не только к её части (и экономия уровня отступа тоже приятна).

Существующие менеджеры контекста, у которых уже есть базовый класс, могут быть расширены путём использования `ContextDecorator` в качестве примесного класса (mixin class):

```python
from contextlib import ContextDecorator

class mycontext(ContextBaseClass, ContextDecorator):
    def __enter__(self):
        return self

    def __exit__(self, *exc):
        return False
```

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

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

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