Что такое контекстный менеджер в Python и для чего он используется
Что проверяют
Умеете ли вы гарантированно освобождать ресурсы. Вопрос почти всегда переходит в «а что будет, если внутри блока выбросится исключение».
Как отвечать
Контекстный менеджер это объект с методами __enter__ и __exit__, который работает с оператором with. Вход в блок вызывает первый метод, выход второй, в том числе при исключении. Именно поэтому файл закрывается, соединение возвращается в пул, а блокировка снимается.
with open("file.txt") as f: # __enter__ вернул файл
data = f.read()
# здесь файл уже закрыт, даже если read() бросил исключениеСвой менеджер пишется двумя способами. Классом:
class Timer:
def __enter__(self):
self.start = time.monotonic()
return self
def __exit__(self, exc_type, exc, tb):
print(f"заняло {time.monotonic() - self.start:.2f}с")
return False # исключение не подавляемИли короче, через генератор:
from contextlib import contextmanager
@contextmanager
def timer():
start = time.monotonic()
try:
yield
finally:
print(f"заняло {time.monotonic() - start:.2f}с")Где применяется: файлы, сетевые соединения, транзакции базы, блокировки, временная подмена настроек в тестах, замер времени.
Где спотыкаются
Не знают, что __exit__ получает информацию об исключении и что возврат True его подавляет. Это частая причина проглоченных ошибок.
Забывают try/finally в генераторном варианте: без него при исключении код после yield не выполнится, и ресурс не освободится.
Не помнят, что with умеет несколько менеджеров сразу (with a() as x, b() as y:), и что для асинхронного кода есть async with с __aenter__ и __aexit__.