async with. Они реализуют методы __aenter__ и __aexit__. __aenter__ получает ресурс, а __aexit__ освобождает его, обеспечивая корректное закрытие соединения даже при исключениях. Например:
async with aiohttp.ClientSession() as session:
async with session.get('https://example.com') as resp:
print(await resp.text())
Контекстные менеджеры в Python предоставляют удобный способ для управления ресурсами, обеспечивая их корректное выделение и освобождение (например, открытие и закрытие файлов, установление и разрыв сетевых соединений). Применительно к асинхронным операциям, стандартные контекстные менеджеры, основанные на __enter__ и __exit__, являются синхронными и не подходят для эффективной работы с асинхронным кодом.
Для работы с асинхронными контекстными менеджерами используется модуль asyncio и ключевые слова async with. Вместо __enter__ и __exit__ используются __aenter__ и __aexit__, которые должны быть объявлены как асинхронные методы (async def).
Вот пример, иллюстрирующий использование асинхронного контекстного менеджера для управления асинхронным подключением к ресурсу (в данном примере, это упрощенный пример, демонстрирующий концепцию):
import asyncio
class AsyncConnection:
def __init__(self, host, port):
self.host = host
self.port = port
self.connection = None
async def connect(self):
# Имитация асинхронного установления соединения
await asyncio.sleep(0.1)
print(f"Подключение к {self.host}:{self.port}")
self.connection = "Connection object" # Замените на реальный объект соединения
async def close(self):
# Имитация асинхронного закрытия соединения
await asyncio.sleep(0.1)
print(f"Закрытие соединения с {self.host}:{self.port}")
self.connection = None
async def __aenter__(self):
await self.connect()
return self # Возвращаем объект, который будет доступен в блоке 'async with'
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.close()
async def main():
async with AsyncConnection("example.com", 8080) as conn:
# Внутри этого блока 'conn' представляет установленное соединение
print("Соединение установлено. Используем...")
# Замените на реальные операции с соединением
await asyncio.sleep(0.2)
print("Соединение больше не нужно.")
# После выхода из блока 'async with', соединение гарантированно закрыто
if __name__ == "__main__":
asyncio.run(main())
Объяснение:
AsyncConnection представляет асинхронное соединение.connect() имитирует установление соединения (используется asyncio.sleep для имитации асинхронной операции). В реальной ситуации здесь был бы код, например, для установления TCP соединения с использованием asyncio.open_connection.close() имитирует закрытие соединения.__aenter__() это асинхронный метод, который вызывается при входе в блок async with. Он устанавливает соединение и возвращает объект соединения (self).__aexit__() это асинхронный метод, который вызывается при выходе из блока async with (даже если возникло исключение). Он закрывает соединение. exc_type, exc_val и exc_tb содержат информацию об исключении, если оно произошло. Если __aexit__ возвращает True, то исключение подавляется.async with AsyncConnection(...) as conn: создает асинхронный контекст, в котором conn представляет установленное соединение. После выхода из этого блока, соединение гарантированно будет закрыто, даже если внутри блока произойдет исключение.Преимущества использования асинхронных контекстных менеджеров:
__aexit__ позволяет корректно обрабатывать исключения, возникшие в блоке async with.В реальных приложениях можно использовать библиотеки, предоставляющие готовые асинхронные реализации, например, для работы с базами данных (например, asyncpg, aiosqlite) или HTTP клиентами (например, aiohttp).