Вопрос проверяет понимание протокола контекстного менеджера в Python и знание специальных методов для его реализации через класс.
Контекстный менеджер в Python — это объект, который определяет контекст выполнения для блока кода, используемого с оператором with. Основная цель — обеспечить корректное выделение и освобождение ресурсов (например, открытие и закрытие файлов, блокировка и разблокировка мьютексов, установление и разрыв соединений с базой данных). Реализация через класс требует определения двух магических методов, которые составляют протокол контекстного менеджера.
Метод __enter__(self) вызывается в момент входа в блок with. Его задача — инициализировать и вернуть ресурс, который будет использоваться внутри блока. Возвращаемое значение может быть как сам объект контекстного менеджера, так и другой объект (например, открытый файловый дескриптор).
Метод __exit__(self, exc_type, exc_val, exc_tb) вызывается при выходе из блока with, независимо от того, завершился ли блок нормально или с исключением. Он принимает три аргумента, связанных с исключением: тип исключения, его значение и объект трассировки. Если исключения не было, все три аргумента равны None. Метод должен освободить ресурсы (например, закрыть файл) и может обработать исключение, вернув True (что подавит исключение), или позволить ему распространиться дальше, вернув False или ничего (по умолчанию возвращается None, что трактуется как False).
Рассмотрим простой контекстный менеджер для измерения времени выполнения блока кода.
import time
class Timer:
def __enter__(self):
self.start_time = time.time()
print("Таймер запущен")
return self # Возвращаем сам объект для доступа к атрибутам
def __exit__(self, exc_type, exc_val, exc_tb):
self.end_time = time.time()
elapsed = self.end_time - self.start_time
print(f"Выполнение заняло {elapsed:.2f} секунд")
# Не подавляем исключения, возвращаем False
return False
# Использование
with Timer() as timer:
time.sleep(1)
# Внутри блока можно обращаться к timer.start_time
print(f("Старт в {timer.start_time}"))Контекстные менеджеры широко используются для управления ресурсами, которые требуют обязательного завершающего действия. Типичные примеры:
open() возвращает контекстный менеджер).stdout).threading.Lock.Реализация через класс предоставляет полный контроль над логикой инициализации и очистки, а также над обработкой ошибок. Это особенно полезно для создания собственных менеджеров ресурсов в библиотеках и фреймворках.
Вывод: Реализуйте контекстный менеджер через класс, когда вам нужно управлять жизненным циклом ресурса с гарантированным выполнением завершающих действий, или когда требуется тонкая настройка обработки исключений в рамках блока with.