Что такое контекстный менеджер. Как написать свойpython-95

Контекстный менеджер — это объект, который определяет контекст выполнения блока кода с помощью оператора with. Он гарантирует, что определенные действия будут выполнены при входе в контекст и при выходе из него, даже если внутри блока возникло исключение.

Основные сценарии использования:

  1. Работа с файлами (автоматическое закрытие)
  2. Работа с соединениями (БД, сетевые)
  3. Блокировки и транзакции
  4. Временные изменения конфигурации
  5. Замеры времени выполнения

Стандартное использование:

with open('file.txt', 'r') as f:
    data = f.read()
# Файл автоматически закрывается здесь, даже если возникло исключение

Как написать свой контекстный менеджер

1. Используя класс

Необходимо реализовать два магических метода:

  • __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)

2. Используя contextlib

Модуль 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()}")

3. Используя contextlib.ContextDecorator

Для создания контекстного менеджера, который можно использовать и как декоратор.

Пример:

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("не число")

Параметры метода exit

  1. exc_type - тип исключения (None, если исключения не было)
  2. exc_val - экземпляр исключения
  3. 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()

Резюмируем

  1. Контекстные менеджеры обеспечивают безопасное выполнение кода с гарантированным завершением
  2. Основные способы создания:
    • Класс с __enter__/__exit__
    • Функция с @contextmanager
    • Наследование от ContextDecorator
  3. Позволяют управлять ресурсами, временными изменениями и исключениями
  4. Поддерживают асинхронный вариант через __aenter__/__aexit__
  5. Широко используются в стандартной библиотеке и популярных фреймворках

Контекстные менеджеры — это мощный инструмент Python для написания чистого, безопасного и поддерживаемого кода работы с ресурсами.