Python中的协程与异步编程中的异步上下文管理器与async with语句
字数 1254 2025-11-25 12:31:54

Python中的协程与异步编程中的异步上下文管理器与async with语句

1. 异步上下文管理器的背景与需求

在异步编程中,常见的资源操作(如文件I/O、数据库连接、网络请求)可能需要等待I/O完成。如果使用传统的同步上下文管理器(如with open(...)),在进入或退出上下文时可能会阻塞事件循环。异步上下文管理器通过__aenter____aexit__方法,允许在异步环境中非阻塞地管理资源。


2. 异步上下文管理器的协议定义

一个类要成为异步上下文管理器,需实现以下两个异步方法:

  1. __aenter__(self): 在进入async with代码块时自动调用,可以执行异步初始化操作(如建立数据库连接)。
  2. __aexit__(self, exc_type, exc_val, exc_tb): 在退出代码块时自动调用,用于异步清理资源(如关闭连接),并能处理异常。

注意

  • 这两个方法必须定义为async def
  • __aexit__的参数用于捕获代码块内可能发生的异常(若未发生异常,这些参数为None)。

3. async with语句的工作流程

以下代码展示了async with的执行顺序:

async with AsyncManager() as resource:
    # 代码块内的操作
    pass

步骤分解

  1. 调用AsyncManager()创建实例。
  2. 自动调用实例的__aenter__方法,并等待其完成(await隐式发生)。
  3. __aenter__的返回值被赋值给resource
  4. 执行代码块内的逻辑。
  5. 无论代码块是否发生异常,都会自动调用__aexit__方法,并等待其完成。
  6. 如果代码块内发生异常,异常信息会传递给__aexit__,由其决定是否处理(返回True则抑制异常,否则异常传播)。

4. 手动实现异步上下文管理器示例

以下是一个模拟数据库连接的异步上下文管理器:

class AsyncDatabaseConnection:
    async def __aenter__(self):
        print("异步连接数据库...")
        await asyncio.sleep(1)  # 模拟异步连接
        self.connection = "模拟的数据库连接"
        return self.connection
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("异步关闭数据库连接...")
        await asyncio.sleep(0.5)  # 模拟异步关闭
        if exc_type:
            print(f"发生异常: {exc_val}")
        # 返回False时,异常会传播;返回True则抑制异常

# 使用示例
async def main():
    async with AsyncDatabaseConnection() as conn:
        print(f"使用连接: {conn}")
        # 模拟数据库操作
        await asyncio.sleep(2)

asyncio.run(main())

输出结果

异步连接数据库...  
使用连接: 模拟的数据库连接  
异步关闭数据库连接...  

5. 使用contextlib简化实现

Python的contextlib模块提供了@asynccontextmanager装饰器,可以用生成器快速创建异步上下文管理器:

from contextlib import asynccontextmanager

@asynccontextmanager
async def async_timer():
    start = asyncio.get_event_loop().time()
    print("开始计时")
    try:
        yield start  # 在此处暂停,执行代码块
    finally:
        end = asyncio.get_event_loop().time()
        print(f"耗时: {end - start:.2f}秒")

async def main():
    async with async_timer() as start_time:
        print(f"起始时间: {start_time}")
        await asyncio.sleep(2)

asyncio.run(main())

输出

开始计时  
起始时间: 12345.67  
耗时: 2.00秒  

6. 常见应用场景

  1. 数据库连接池:如async with pool.acquire() as conn
  2. 异步文件I/O:如aiofiles库的async with aiofiles.open(...) as f
  3. 网络请求会话:如aiohttpasync with aiohttp.ClientSession() as session
  4. 事务管理:在异步ORM中管理数据库事务的提交与回滚。

7. 注意事项

  • 若在__aenter____aexit__中忘记使用await,可能导致资源未正确初始化或清理。
  • 异步上下文管理器必须用在异步函数内(async def),否则会报语法错误。
  • 与同步上下文管理器(__enter__/__exit__)协议不同,二者不能混用。
Python中的协程与异步编程中的异步上下文管理器与async with语句 1. 异步上下文管理器的背景与需求 在异步编程中,常见的资源操作(如文件I/O、数据库连接、网络请求)可能需要等待I/O完成。如果使用传统的同步上下文管理器(如 with open(...) ),在进入或退出上下文时可能会阻塞事件循环。异步上下文管理器通过 __aenter__ 和 __aexit__ 方法,允许在异步环境中非阻塞地管理资源。 2. 异步上下文管理器的协议定义 一个类要成为异步上下文管理器,需实现以下两个异步方法: __aenter__(self) : 在进入 async with 代码块时自动调用,可以执行异步初始化操作(如建立数据库连接)。 __aexit__(self, exc_type, exc_val, exc_tb) : 在退出代码块时自动调用,用于异步清理资源(如关闭连接),并能处理异常。 注意 : 这两个方法必须定义为 async def 。 __aexit__ 的参数用于捕获代码块内可能发生的异常(若未发生异常,这些参数为 None )。 3. async with语句的工作流程 以下代码展示了 async with 的执行顺序: 步骤分解 : 调用 AsyncManager() 创建实例。 自动调用实例的 __aenter__ 方法,并等待其完成( await 隐式发生)。 __aenter__ 的返回值被赋值给 resource 。 执行代码块内的逻辑。 无论代码块是否发生异常,都会自动调用 __aexit__ 方法,并等待其完成。 如果代码块内发生异常,异常信息会传递给 __aexit__ ,由其决定是否处理(返回 True 则抑制异常,否则异常传播)。 4. 手动实现异步上下文管理器示例 以下是一个模拟数据库连接的异步上下文管理器: 输出结果 : 5. 使用contextlib简化实现 Python的 contextlib 模块提供了 @asynccontextmanager 装饰器,可以用生成器快速创建异步上下文管理器: 输出 : 6. 常见应用场景 数据库连接池 :如 async with pool.acquire() as conn 。 异步文件I/O :如 aiofiles 库的 async with aiofiles.open(...) as f 。 网络请求会话 :如 aiohttp 的 async with aiohttp.ClientSession() as session 。 事务管理 :在异步ORM中管理数据库事务的提交与回滚。 7. 注意事项 若在 __aenter__ 或 __aexit__ 中忘记使用 await ,可能导致资源未正确初始化或清理。 异步上下文管理器必须用在异步函数内( async def ),否则会报语法错误。 与同步上下文管理器( __enter__ / __exit__ )协议不同,二者不能混用。