Вопрос проверяет понимание создания контекстных менеджеров в Python с использованием генераторов и декоратора @contextlib.contextmanager, что необходимо для управления ресурсами и временем жизни объектов.
Контекстные менеджеры в Python используются для управления ресурсами, гарантируя, что определённые действия будут выполнены до и после блока кода, например, закрытие файла или откат транзакции. Классический способ — создать класс с методами __enter__ и __exit__, но для простых случаев можно использовать генератор с декоратором @contextlib.contextmanager из модуля contextlib.
Декоратор @contextlib.contextmanager преобразует генераторную функцию в контекстный менеджер. Внутри функции:
yield выполняется при входе в блок with (эквивалент __enter__).yield, становится значением, возвращаемым контекстным менеджером (если используется as).yield выполняется при выходе из блока with (эквивалент __exit__), независимо от того, было ли исключение.Рассмотрим пример контекстного менеджера для временного изменения текущей рабочей директории:
import os
from contextlib import contextmanager
@contextmanager
def temporary_cd(path):
"""Меняет текущую директорию на время выполнения блока with."""
old_cwd = os.getcwd() # Код до yield — вход
os.chdir(path)
try:
yield # Здесь выполняется код внутри блока with
finally:
os.chdir(old_cwd) # Код после yield — выход, гарантирует возврат
# Использование
with temporary_cd('/tmp'):
print(os.getcwd()) # Выведет /tmp
print(os.getcwd()) # Вернётся к исходной директорииТакой подход удобен для:
Контекстный менеджер может обрабатывать исключения в блоке после yield. Например, менеджер для измерения времени выполнения:
import time
from contextlib import contextmanager
@contextmanager
def timer():
start = time.time()
try:
yield
finally:
end = time.time()
print(f'Execution time: {end - start:.2f} seconds')
with timer():
time.sleep(1) # Имитация долгой операцииВывод: Execution time: 1.00 seconds. Код в finally гарантирует, что время будет выведено даже при возникновении исключения внутри блока.
Создание контекстного менеджера через генератор с @contextlib.contextmanager — это лаконичный и читаемый способ для простых сценариев управления ресурсами. Используйте этот подход, когда не требуется сложная логика в методах __enter__/__exit__, чтобы избежать избыточного кода.