Вопрос проверяет понимание работы контекстных менеджеров в Python, которые используются для управления ресурсами и обеспечения корректного их освобождения.
Контекстные менеджеры в Python — это объекты, которые определяют контекст выполнения для блока кода, созданного оператором with. Их основная цель — управление ресурсами: гарантировать, что ресурс будет корректно выделен и, что более важно, освобождён после использования, даже если в процессе работы произошла ошибка.
Когда интерпретатор Python встречает оператор with, он выполняет следующие шаги:
with (обычно это создание объекта контекстного менеджера).__enter__(). Значение, возвращённое этим методом, присваивается переменной, указанной после as (если она есть).with.__exit__() того же объекта.Самый распространённый пример — работа с файлами. Вместо ручного открытия и закрытия файла с проверкой на исключения, можно написать:
with open('data.txt', 'r') as file:
content = file.read()
# Файл автоматически закроется здесь, даже если read() вызовет ошибку
За кулисами класс файлового объекта реализует методы __enter__ и __exit__. Метод __enter__ возвращает сам файловый объект, а __exit__ вызывает file.close().
Вы можете создать свой контекстный менеджер, определив класс с двумя обязательными методами.
class TimerContext:
def __enter__(self):
import time
self.start = time.time()
print("Таймер запущен")
return self # Можно вернуть объект для использования в блоке 'as'
def __exit__(self, exc_type, exc_val, exc_tb):
import time
elapsed = time.time() - self.start
print(f"Таймер остановлен. Прошло {elapsed:.2f} секунд")
# Если вернуть True, исключение будет подавлено
return False
# Использование
with TimerContext() as timer:
import time
time.sleep(1)
print("Работа внутри блока")
Метод __exit__ получает три аргумента с информацией об исключении (если оно возникло). Если метод возвращает True, исключение считается обработанным и не распространяется дальше. Это полезно для подавления определённых ошибок или выполнения отката транзакций.
Вывод: Контекстные менеджеры следует применять всегда, когда работа с ресурсом требует парных действий «открыть/закрыть», «заблокировать/разблокировать» или «начать/завершить». Они делают код безопаснее, читабельнее и избавляют от дублирования кода обработки ошибок.