Короткий ответ
Класс-менеджер определяет методы __enter__ (код настройки, возвращаемое значение становится переменной after as) и __exit__ (код очистки, получает информацию об исключении и может его подавить, вернув True); contextlib.contextmanager превращает функцию-генератор с одним yield в контекстный менеджер, где код до yield — это __enter__, а код после yield (обычно в try/finally) — это __exit__.
Как это работает подробнее
Блок with cm: вызывает cm.__enter__() и привязывает его результат к переменной после as. При выходе из блока (нормально или из-за исключения) вызывается cm.__exit__(exc_type, exc_value, traceback); если исключение было, но __exit__ возвращает truthy-значение, исключение считается обработанным и не распространяется дальше. У генератора с @contextmanager исключение из блока with "прорастает" в точку yield внутри генератора, поэтому код очистки обычно оборачивают в try/finally вокруг yield.
Пример кода
from contextlib import contextmanager
class Timer:
def __enter__(self):
import time
self._start = time.perf_counter()
return self
def __exit__(self, exc_type, exc_value, tb):
import time
print(f"Прошло: {time.perf_counter() - self._start:.4f} с")
return False # не подавляем исключение
@contextmanager
def timer_gen():
import time
start = time.perf_counter()
try:
yield
finally:
print(f"Прошло: {time.perf_counter() - start:.4f} с")
with Timer():
sum(range(100_000))
with timer_gen():
sum(range(100_000))Что использовать на практике
- Практика. Класс выбирают, когда менеджеру нужно хранить сложное состояние или предоставлять дополнительные методы.
@contextmanager— более короткий способ для простых случаев (временная подмена настройки, блокировка, замер времени), особенно когда логика естественно описывается как "код до" /yield/ "код после".
Подводные камни
- Риски. Забывают try/finally вокруг yield в генераторной версии, из-за чего код очистки не выполняется при исключении внутри with. В классовой версии забывают, что
__exit__, возвращающийTrue, подавляет исключение — иногда это делают случайно и теряют важную ошибку.