Этот вопрос проверяет, умеете ли вы безопасно получать и освобождать ресурсы (файлы, соединения, блокировки) и понимаете ли, зачем нужен with.
Контекстный менеджер — это способ гарантированно “убрать за собой” после работы с ресурсом. Он используется через with, чтобы ресурс корректно освобождался даже при ошибках. Например, файл будет закрыт, соединение — возвращено в пул, а блокировка — отпущена. Это уменьшает утечки ресурсов и делает код проще и надежнее.
Контекстные менеджеры решают частую проблему: ресурс нужно освободить всегда, независимо от того, успешно завершился код или упал с исключением.
Определение: Контекстный менеджер — объект, который определяет, что сделать при входе в блок with и что сделать при выходе из него, включая случаи с исключениями.
Перед началом перечисления важно понять идею: ресурс берется “на время”, и освобождение должно происходить автоматически.
Гарантия освобождения
Файл закроется даже если внутри with случится ошибка.
Блокировка будет отпущена, чтобы не повесить программу.
Код становится проще
Не нужно помнить try/finally в каждом месте.
Меньше шаблонного кода, меньше ошибок.
Единый стиль работы с ресурсами
Файлы, сетевые соединения, транзакции, блокировки — все выглядит одинаково через with.
with open("data.txt", "r", encoding="utf-8") as f:
text = f.read()
# здесь файл уже закрыт автоматически
with (чтобы понять, что именно экономим)f = open("data.txt", "r", encoding="utf-8")
try:
text = f.read()
finally:
f.close()
Контекстный менеджер обычно реализует два метода.
__enter__ — выполняется при входе в with и может вернуть объект (попадает в as ...).
__exit__ — выполняется при выходе и получает информацию об исключении (если оно было).
Пример простого собственного контекстного менеджера:
import time
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, exc_type, exc, tb):
elapsed = time.perf_counter() - self.start
print(f"Elapsed: {elapsed:.4f}s")
return False # исключение не “глотать”
with Timer():
sum(range(1_000_00))
Файлы: open(...)
Блокировки: threading.Lock() / asyncio.Lock()
Соединения: работа с пулом соединений (взять/вернуть)
Транзакции: начать/закоммитить/откатить
Контекстные менеджеры стоит применять всегда, когда ресурс нужно освобождать гарантированно: это делает код короче, безопаснее и устойчивее к ошибкам.