Гарантированная уборка: __enter__/__exit__ и contextlib.
⏱️ ~1 ч 30 мин·Средний·📚 6 уроков
Бытовая аналогия: аренда переговорки
Когда вы бронируете переговорку, происходит две вещи: на входе вам выдают ключ и включают свет, а на выходе — забирают ключ и гасят свет. Причём убрать за собой нужно, даже если встреча прошла ужасно и вы выбежали в гневе.
Менеджер контекста — это и есть такой администратор: он гарантированно выполняет «вход» и «выход», что бы ни случилось внутри. Именно поэтому with open(...) закрывает файл сам — даже если внутри блока случилась ошибка.
Зачем это нужно
Ресурсы (файлы, сетевые соединения, блокировки) нужно освобождать. Если сделать это вручную, легко забыть — особенно когда посреди работы вылетает исключениеСигнал об ошибке во время выполнения. Его можно перехватить блоком try/except.. Конструкция with решает проблему: код «выхода» выполнится всегда.
▶ with-vs-manual.py
Свой менеджер контекста: __enter__ и __exit__
Любой объектКонкретный экземпляр класса — со своими данными. Почти всё в Python является объектом. с методами __enter__ и __exit__ можно использовать в with:
__enter__(self) — выполняется на входе; то, что он вернёт, попадёт в переменнуюИменованное хранилище для значения. Имя связывается со значением через присваивание: x = 5. после as.
__exit__(self, exc_type, exc_val, exc_tb) — выполняется на выходе всегда, даже при исключении. В аргументыЗначение, которое передаётся функции при вызове. Внутри функции оно доступно как параметр. приходит информация об ошибке (или None, если её не было).
▶ custom-cm.py
__exit__ срабатывает и при ошибке
Даже если внутри with вылетело исключение, __exit__ всё равно вызовется — это и есть гарантия уборки.
▶ exit-on-error.py
Короткий способ: contextlib.contextmanager
Писать целый классШаблон для создания объектов: описывает их данные (атрибуты) и поведение (методы). ради двух действий громоздко. Декоратор @contextmanager из модуля contextlib позволяет описать менеджер контекста обычной функциейИменованный блок кода, который можно вызывать многократно. Может принимать аргументы и возвращать результат. с одним yield: всё до yield — это «вход», всё после — «выход».
▶ contextlib-cm.py
💡Совет
finally внутри @contextmanager играет роль __exit__: код после yield в блоке finally выполнится, даже если внутри with случилась ошибка.
💡Совет
🔮 Предскажите вывод
Помните: __enter__ — на входе, __exit__ — на выходе всегда. В каком порядке напечатаются строкиТекстовое значение в кавычках: "привет". Неизменяема. По-английски string (str).?
▶ predict-cm.py
Разбор. Сначала выполняется __enter__ → печатает A. Затем тело блока with → B. На выходе (даже без ошибок) вызывается __exit__ → C. Порядок: A, B, C.
⚠️ Частые ошибки новичков
Забывают вернуть значение из __enter__ — тогда после as окажется None. Чтобы получить сам объект, верните self.
Ставят несколько yield в функции с @contextmanager — их должно быть ровно один.
Делают уборку без try/finally в @contextmanager — тогда при ошибке в теле with код после yield не выполнится.
▶ broken-enter.py
Правильно — вернуть объект: в конце __enter__ написать return self. Тогда c будет ссылаться на сам менеджер контекста.
Заданиерешение.py
Напишите класс-менеджер контекста Bracket, который при входе добавляет строку "[" в список log, а при выходе — "]". Список передаётся в конструктор. Метод __enter__ должен вернуть сам объект.
Заданиерешение.py
Используя декоратор @contextmanager из contextlib, напишите менеджер контекста banner(log), который добавляет в список log строку "start" на входе и "end" на выходе (в блоке finally, чтобы уборка сработала при любой ошибке).
❓Проверь себя
Когда вызывается метод __exit__ менеджера контекста?
Что попадёт в переменную после as во with ... as x?
Как @contextmanager из contextlib разделяет «вход» и «выход»?
ℹ️Важно
✅ Что вы узнали
Менеджер контекста гарантирует «уборку»: код выхода выполнится даже при ошибке в блоке with.
Свой менеджер = класс с __enter__ (возвращает объект для as) и __exit__ (освобождает ресурс).
@contextmanager из contextlib позволяет описать менеджер функцией с одним yield и try/finally.
Именно поэтому with open(...) сам закрывает файл — под капотом это менеджер контекста.
Комментарии
Загрузка…
Загрузка комментариев…