Асинхронные контекстные менеджеры в Python (введенные в Python 3.7 с помощью библиотеки asyncio
) позволяют безопасно и удобно управлять асинхронными ресурсами, такими как асинхронные соединения, асинхронные файловые операции или любые другие объекты, требующие асинхронной инициализации и завершения.
Основной механизм заключается в использовании двух специальных методов:
__aenter__(self)
: Этот метод вызывается при входе в блок async with
. Он должен быть корутиной (т.е., определен с помощью async def
). Он выполняет асинхронные операции настройки, такие как установление соединения или открытие файла. Он может возвращать значение, которое будет присвоено переменной, указанной после as
в операторе async with
. Если он вызывает исключение, блок async with
не будет выполнен.__aexit__(self, exc_type, exc_val, exc_tb)
: Этот метод вызывается при выходе из блока async with
, независимо от того, был ли он завершен нормально или с исключением. Он также должен быть корутиной. Он выполняет асинхронные операции завершения, такие как закрытие соединения или закрытие файла. Принимает три аргумента:exc_type
: Тип исключения, если таковое возникло, иначе None
.exc_val
: Экземпляр исключения, если таковое возникло, иначе None
.exc_tb
: Трассировка стека исключения, если таковое возникло, иначе None
.Если __aexit__
возвращает True
, исключение, если оно было, подавляется (не распространяется дальше). Если возвращается None
или любое другое значение, исключение распространяется как обычно.
Пример:
import asyncio
class AsyncResource:
async def open(self):
print("Opening resource...")
await asyncio.sleep(0.1) # Simulate async operation
print("Resource opened.")
async def close(self):
print("Closing resource...")
await asyncio.sleep(0.1) # Simulate async operation
print("Resource closed.")
class AsyncContextManager:
def __init__(self, resource):
self.resource = resource
async def __aenter__(self):
print("Entering context...")
await self.resource.open()
return self.resource # Возвращаем ресурс, который будет доступен внутри блока async with
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("Exiting context...")
await self.resource.close()
print("Context exited.")
async def main():
resource = AsyncResource()
async with AsyncContextManager(resource) as res:
print("Inside the async with block...")
# Используем ресурс res (который равен resource)
await asyncio.sleep(0.2)
print("Doing something with the resource...")
print("After the async with block.")
if __name__ == "__main__":
asyncio.run(main())
Ключевые моменты:
__aenter__
и __aexit__
должны быть асинхронными.__aexit__
обеспечивает надежную обработку исключений, гарантируя освобождение ресурсов даже в случае ошибок.async with
, __aexit__
всегда будет вызван для очистки.