Урок курса
Контекстный менеджер и оператор with в Python
Python для профессионаловОператор with задаёт участок кода с гарантированным входом и выходом. Самый знакомый пример — файл: он будет закрыт и при успехе, и при исключении.
with open("report.txt", "w", encoding="utf-8") as file:
file.write("ready")
Объект справа от with реализует протокол контекстного менеджера. Метод __enter__ подготавливает ресурс и возвращает значение после as. Метод __exit__ получает сведения об исключении и освобождает ресурс. Это структурированный аналог try/finally, который сложнее забыть.
Собственный контекстный менеджер
class Timer:
def __enter__(self):
from time import perf_counter
self.started = perf_counter()
return self
def __exit__(self, exc_type, exc, traceback):
from time import perf_counter
self.elapsed = perf_counter() - self.started
return False
with Timer() as timer:
calculate()
print(timer.elapsed)
Если __exit__ возвращает истинное значение, исключение считается обработанным. Обычно возвращают False или None, чтобы не проглотить ошибку случайно. Освобождение ресурса должно выполняться даже при частично завершённой операции.
contextlib и область применения
Для простых случаев класс не обязателен. Декоратор contextlib.contextmanager превращает генератор с одним yield в контекстный менеджер: код до yield выполняет вход, блок finally после него — выход.
Контекстные менеджеры подходят для файлов, транзакций, временной смены настроек, блокировок и сетевых соединений. Они не делают операцию атомарной сами по себе: например, менеджер транзакции должен явно вызвать commit или rollback по контракту конкретной библиотеки.
Главный критерий — ресурс или состояние имеют парные действия «получить/освободить». Тогда with фиксирует границы владения прямо в структуре программы и уменьшает риск утечек.
Cleanup срабатывает даже при исключении
Минимальный контекстный менеджер открывает ресурс до yield и освобождает его в finally. Покажем поведение на списке событий, чтобы запуск примера не требовал файлов или сетевого сервиса. Исключение внутри with продолжает распространяться, если менеджер его не подавляет.
from contextlib import contextmanager
events = []
@contextmanager
def resource():
events.append("open")
try:
yield "connection"
finally:
events.append("close")
try:
with resource() as value:
assert value == "connection"
raise ValueError("Учебная ошибка")
except ValueError:
events.append("caught")
print(events)
assert events == ["open", "close", "caught"]
Прежде чем внешний except обработает ошибку, выполняется finally менеджера. Менеджер управляет освобождением, но не делает любую операцию атомарной и не откатывает автоматически изменение базы. Транзакцию и повторяемость нужно проектировать отдельно. Контракт contextmanager в Python.
Продолжите разбор: Декораторы в Python: функции, @ и параметры.
Задача 1
Какое значение exit обычно возвращает, если исключение не нужно подавлять?
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
