ПрограммированиеPython разработчик (asyncio/Backend)

Что такое менеджер контекста Async (async context manager) в Python, как его реализовать и где это бывает незаменимо?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Менеджер контекста Async — это объект, который определяет асинхронные методы __aenter__ и __aexit__ и используется в конструкции async with. Такой менеджер нужен для корректного открытия/закрытия ресурсов в асинхронных функциях: соединений с БД, файлов, сессий и т.д.

Пример реализации:

class AsyncDBConnection: async def __aenter__(self): self.conn = await async_db_connect() return self.conn async def __aexit__(self, exc_type, exc, tb): await self.conn.close() async def main(): async with AsyncDBConnection() as conn: await conn.query('SELECT 1')

Асинхронный контекст позволяет не блокировать event loop техническими задержками, повышая производительность concurrent-программ.

Вопрос с подвохом.

Можно ли использовать обычный with внутри async def?

Ответ: Да, но если операции внутри менеджера контекста используют awaitable (требуют await), нужен именно async with. Обычный with не поддерживает асинхронный enter/exit, будет блокировать event loop/ или упадёт, если вызвать await внутри enter/exit.

Пример:

async def foo(): with open('file.txt') as f: # это ок, чтение синхронное data = f.read() # Но нельзя await внутри обычного контекста, только внутри async with

Примеры реальных ошибок из-за незнания тонкостей темы.


История

Проект: Веб-сервис с асинхронным API.

Проблема: Менеджер подключения к базе использовал обычный with, но внутри был вызван await. Это привело к ошибке RuntimeError("Cannot use 'await' outside async function") и блокировкам event loop в проде.


История

Проект: Чат на Websockets.

Проблема: При работе с соединениями не закрывались websocket-ресурсы (использовали обычный синхронный менеджер), что приводило к утечкам памяти и зависшим соединениям.


История

Проект: Многопоточная асинхронная очередь заданий.

Проблема: Менеджер задач некорректно реализовал __aexit__, забыли возвращать awaitable. Из-за этого завершение задач происходило не гарантировано, и часть заданий "зависала" в системе.