Контекстный менеджер — это объект, который определяет контекст выполнения блока кода с помощью оператора with
. Он гарантирует, что определенные действия будут выполнены при входе в контекст и при выходе из него, даже если внутри блока возникло исключение.
with open('file.txt', 'r') as f:
data = f.read()
# Файл автоматически закрывается здесь, даже если возникло исключение
Необходимо реализовать два магических метода:
__enter__()
- выполняется при входе в контекст__exit__()
- выполняется при выходе из контекстаПример таймера:
import time
class Timer:
def __enter__(self):
self.start = time.time()
return self # Можно вернуть объект для использования в as
def __exit__(self, exc_type, exc_val, exc_tb):
self.elapsed = time.time() - self.start
print(f"Выполнение заняло {self.elapsed:.2f} секунд")
# Если вернуть True, исключения будут подавлены
with Timer() as t:
# Долгая операция
time.sleep(1)
Модуль contextlib
предоставляет декоратор @contextmanager
для создания контекстных менеджеров из функций-генераторов.
Пример временного изменения директории:
from contextlib import contextmanager
import os
@contextmanager
def temporary_dir(path):
old_dir = os.getcwd()
os.chdir(path)
try:
yield # Здесь выполняется код внутри with
finally:
os.chdir(old_dir) # Всегда возвращаемся обратно
with temporary_dir('/tmp'):
print(f"Текущая директория: {os.getcwd()}")
Для создания контекстного менеджера, который можно использовать и как декоратор.
Пример:
from contextlib import ContextDecorator
class suppress_exception(ContextDecorator):
def __init__(self, exception_type):
self.exception_type = exception_type
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None and issubclass(exc_type, self.exception_type):
return True # Подавляем исключение
# Использование как контекстного менеджера
with suppress_exception(ZeroDivisionError):
1 / 0
# Использование как декоратора
@suppress_exception(ValueError)
def bad_function():
return int("не число")
exc_type
- тип исключения (None, если исключения не было)exc_val
- экземпляр исключенияexc_tb
- traceback-объектЕсли __exit__
возвращает True, исключение будет подавлено.
Для async/await существуют аналогичные методы __aenter__
и __aexit__
.
Пример:
class AsyncConnection:
async def __aenter__(self):
await self.connect()
return self
async def __aexit__(self, exc_type, exc, tb):
await self.close()
async with AsyncConnection() as conn:
await conn.send_data()
__enter__
/__exit__
@contextmanager
ContextDecorator
__aenter__
/__aexit__
Контекстные менеджеры — это мощный инструмент Python для написания чистого, безопасного и поддерживаемого кода работы с ресурсами.