Python 中的异步上下文生成器 @asynccontextmanager详解

作者:

文章目录
  • 在异步代码中,我们会用 async with 语句管理资源(比如异步连接数据库、异步打开文件、异步网络会话),而能被 async with 使用的对象,就是异步上下文管理器。 手动定义一个异步上下文管理器,需要实现两个异步魔法方法: __aenter__():进入 async with 时执行,返回要操作的资源(比如数据库连接); __aexit__():退出 async with 时执行,负责释放资源(比如关闭连接、处理异常)。 手动实现的缺点是代码繁琐,而 @asynccontextmanager 就是为了解决这个问题而生的。
  • from contextlib import asynccontextmanager import asyncio # 用@asynccontextmanager装饰异步生成器函数 @asynccontextmanager async def 异步上下文管理器名(参数): # 1. 进入逻辑:初始化/获取异步资源(对应__aenter__) 资源 = 等待异步操作获取资源() try: yield 资源 # 把资源返回给async with … as 变量,暂停执行 finally: # 2. 退出逻辑:释放/清理资源(对应__aexit__,无论是否异常都会执行) 等待异步操作释放资源(资源) # 调用:用async with语句 async def main(): async with 异步上下文管理器名(参数) as 资源变量: # 操作资源 await 资源变量.异步方法() # 运行异步主函数 asyncio.run(main()) ✅ 关键:必须用 try…finally 包裹 yield,确保即使上下文内代码抛出异常,资源清理逻辑也能执行。
  • 用 @asynccontextmanager 实现一个简单的异步文件操作上下文管理器(模拟异步文件IO,实际Python3.7+有原生asyncio.open): from contextlib import asynccontextmanager import asyncio # 定义异步上下文管理器:异步打开/关闭文件 @asynccontextmanager async def async_open(file_path, mode=”r”): print(“进入上下文:异步打开文件”) # 模拟异步打开文件(实际是同步,仅作演示) file = open(file_path, mode, encoding=”utf-8″) try: yield file # 返回文件对象,供async with使用 finally: print(“退出上下文:异步关闭文件”) file.close() # 清理资源:关闭文件 # 调用异步上下文管理器 async def read_file(): async with async_open(“test.txt”, “w”) as f: await asyncio.sleep(0.1) # 模拟异步写操作 f.write(“Hello @asynccontextmanager!”) print(“文件操作完成,资源已释放”) # 运行异步代码 asyncio.run(read_file()) 执行结果: 进入上下文:异步打开文件退出上下文:异步关闭文件文件操作完成,资源已释放 可以看到:进入async with时执行yield前的代码,退出时执行yield后的清理代码。
  • 实际开发中,@asynccontextmanager 最常用在异步数据库、异步Redis、异步网络请求等场景,比如模拟异步MySQL连接: from contextlib import asynccontextmanager import asyncio # 模拟异步数据库客户端 class AsyncMySQLClient: async def connect(self): print(“建立异步数据库连接”) return self async def close(self): print(“关闭异步数据库连接”) async def query(self, sql): print(f”执行异步SQL:{sql}”) return [{“id”: 1, “name”: “test”}] # 定义异步数据库连接的上下文管理器 @asynccontextmanager async def get_mysql_conn(): # 1. 进入逻辑:建立异步连接 client = AsyncMySQLClient() conn = await client.connect() try: yield conn # 返回连接对象,供业务代码使用 finally: # 2. 退出逻辑:关闭连接,释放资源 await conn.close() # 业务代码:使用数据库连接 async def query_data(): async with get_mysql_conn() as conn: result = await conn.query(“SELECT * FROM user”) print(f”查询结果:{result}”) # 运行 asyncio.run(query_data()) 执行结果: 建立异步数据库连接执行异步SQL:SELECT * FROM user查询结果:[{'id': 1, 'name': 'test'}]关闭异步数据库连接 无论query中是否抛出异常,close都会执行,确保数据库连接被释放。
  • 装饰的必须是异步生成器:函数必须加 async def,且内部有 yield(普通生成器不行,普通生成器用@contextmanager); 异常处理:yield 处可能抛出上下文内的异常,必须用try…finally,否则异常会跳过清理逻辑; 与同步版的区别:同步上下文管理器用 @contextmanager(装饰普通生成器),异步用 @asynccontextmanager(装饰异步生成器),前者配合with,后者配合async with; 返回值:yield 资源 是可选的,如果上下文不需要返回资源,直接yield即可(async with 管理器: 不带as)。
  • 用手动实现异步上下文管理器的方式重写上面的「异步文件管理器」,对比代码量: import asyncio # 手动实现异步上下文管理器(需实现__aenter__/__aexit__) class AsyncOpen: def __init__(self, file_path, mode=”r”): self.file_path = file_path self.mode = mode self.file = None # 异步进入方法 async def __aenter__(self): print(“进入上下文:异步打开文件”) self.file = open(self.file_path, self.mode, encoding=”utf-8″) return self.file # 异步退出方法(exc_type/val/tb 接收异常信息) async def __aexit__(self, exc_type, exc_val, exc_tb): print(“退出上下文:异步关闭文件”) if self.file: self.file.close() # 调用方式不变 async def read_file(): async with AsyncOpen(“test.txt”, “w”) as f: f.write(“手动实现异步上下文管理器”) asyncio.run(read_file()) 可以明显看到:手动实现需要写大量模板代码,而@asynccontextmanager用一个异步生成器就搞定,代码简洁数倍。
  • @asynccontextmanager 是 Python contextlib 模块提供的异步上下文管理器装饰器,专为异步编程设计; 核心是将异步生成器函数转为异步上下文管理器,yield 前是「进入逻辑」,yield 后是「退出清理逻辑」; 必须配合 async with 使用,且生成器内要加 try…finally 保证资源必被清理; 对比手动实现 __aenter__/__aexit__,大幅简化异步资源管理的代码,是异步开发中管理临时资源(连接、文件、会话)的最佳实践之一; 同步场景用它的兄弟装饰器 @contextmanager(配合with,装饰普通生成器),异步场景用@asynccontextmanager(配合async with,装饰异步生成器)。 到此这篇关于Python 中的异步上下文生成器 @asynccontextmanager的文章就介绍到这了,更多相关Python 异步上下文生成器 @asynccontextmanager内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: 基于Python开发一个小说图片PDF生成器 Python使用生成器轻松处理海量数据 Python可迭代对象、迭代器与生成器实现示例详解 python生成器函数yield与yield from使用 使用Python构建智能BAT文件生成器的完美解决方案 Python实现中文数字与阿拉伯数字映射生成器(支持0-9999)的完整代码 Python实现安全密码生成器的示例代码
  • 目录
    • 先铺垫:异步上下文管理器的基础
      • @asynccontextmanager 核心作用
    • 基本使用语法
      • 实际示例:异步文件管理器
        • 进阶示例:异步数据库连接(模拟)
          • 关键注意点
            • 对比:手动实现 vs @asynccontextmanager
              • 总结

                Python 中的 @asynccontextmanager 装饰器,它是异步编程中用于快速定义异步上下文管理器的工具,基于 contextlib 模块实现,核心作用是让你用异步生成器(async yield) 替代手动定义异步上下文管理器的两个魔法方法,大幅简化代码。

                在异步代码中,我们会用 async with 语句管理资源(比如异步连接数据库、异步打开文件、异步网络会话),而能被 async with 使用的对象,就是异步上下文管理器

                手动定义一个异步上下文管理器,需要实现两个异步魔法方法:

                1. __aenter__():进入 async with 时执行,返回要操作的资源(比如数据库连接);
                2. __aexit__():退出 async with 时执行,负责释放资源(比如关闭连接、处理异常)。

                手动实现的缺点是代码繁琐,而 @asynccontextmanager 就是为了解决这个问题而生的。

                通过这个装饰器,你可以用一个异步生成器函数,直接定义出异步上下文管理器的「进入逻辑」和「退出逻辑」,无需手动实现 __aenter____aexit__

                • 生成器中 yield 之前的代码 → 对应 __aenter__() 的逻辑(进入上下文,初始化/获取资源);
                • 生成器中 yield 之后的代码 → 对应 __aexit__() 的逻辑(退出上下文,释放/清理资源);
                • yield 后面的值 → 就是 async with ... as 变量 中「变量」接收到的资源对象。

                from contextlib import asynccontextmanager
                import asyncio
                # 用@asynccontextmanager装饰异步生成器函数
                @asynccontextmanager
                async def 异步上下文管理器名(参数):
                    # 1. 进入逻辑:初始化/获取异步资源(对应__aenter__)
                    资源 = 等待异步操作获取资源()
                    try:
                        yield 资源  # 把资源返回给async with ... as 变量,暂停执行
                    finally:
                        # 2. 退出逻辑:释放/清理资源(对应__aexit__,无论是否异常都会执行)
                        等待异步操作释放资源(资源)
                # 调用:用async with语句
                async def main():
                    async with 异步上下文管理器名(参数) as 资源变量:
                        # 操作资源
                        await 资源变量.异步方法()
                # 运行异步主函数
                asyncio.run(main())

                ✅ 关键:必须用 try...finally 包裹 yield,确保即使上下文内代码抛出异常,资源清理逻辑也能执行。

                @asynccontextmanager 实现一个简单的异步文件操作上下文管理器(模拟异步文件IO,实际Python3.7+有原生asyncio.open):

                from contextlib import asynccontextmanager
                import asyncio
                # 定义异步上下文管理器:异步打开/关闭文件
                @asynccontextmanager
                async def async_open(file_path, mode="r"):
                    print("进入上下文:异步打开文件")
                    # 模拟异步打开文件(实际是同步,仅作演示)
                    file = open(file_path, mode, encoding="utf-8")
                    try:
                        yield file  # 返回文件对象,供async with使用
                    finally:
                        print("退出上下文:异步关闭文件")
                        file.close()  # 清理资源:关闭文件
                # 调用异步上下文管理器
                async def read_file():
                    async with async_open("test.txt", "w") as f:
                        await asyncio.sleep(0.1)  # 模拟异步写操作
                        f.write("Hello @asynccontextmanager!")
                    print("文件操作完成,资源已释放")
                # 运行异步代码
                asyncio.run(read_file())

                执行结果

                进入上下文:异步打开文件
                退出上下文:异步关闭文件
                文件操作完成,资源已释放

                可以看到:进入async with时执行yield前的代码,退出时执行yield后的清理代码。

                实际开发中,@asynccontextmanager 最常用在异步数据库、异步Redis、异步网络请求等场景,比如模拟异步MySQL连接:

                from contextlib import asynccontextmanager
                import asyncio
                # 模拟异步数据库客户端
                class AsyncMySQLClient:
                    async def connect(self):
                        print("建立异步数据库连接")
                        return self
                    async def close(self):
                        print("关闭异步数据库连接")
                    async def query(self, sql):
                        print(f"执行异步SQL:{sql}")
                        return [{"id": 1, "name": "test"}]
                # 定义异步数据库连接的上下文管理器
                @asynccontextmanager
                async def get_mysql_conn():
                    # 1. 进入逻辑:建立异步连接
                    client = AsyncMySQLClient()
                    conn = await client.connect()
                    try:
                        yield conn  # 返回连接对象,供业务代码使用
                    finally:
                        # 2. 退出逻辑:关闭连接,释放资源
                        await conn.close()
                # 业务代码:使用数据库连接
                async def query_data():
                    async with get_mysql_conn() as conn:
                        result = await conn.query("SELECT * FROM user")
                        print(f"查询结果:{result}")
                # 运行
                asyncio.run(query_data())

                执行结果

                建立异步数据库连接
                执行异步SQL:SELECT * FROM user
                查询结果:[{'id': 1, 'name': 'test'}]
                关闭异步数据库连接

                无论query中是否抛出异常,close都会执行,确保数据库连接被释放。

                1. 装饰的必须是异步生成器:函数必须加 async def,且内部有 yield(普通生成器不行,普通生成器用@contextmanager);
                2. 异常处理:yield 处可能抛出上下文内的异常,必须用try...finally,否则异常会跳过清理逻辑;
                3. 与同步版的区别:同步上下文管理器用 @contextmanager(装饰普通生成器),异步用 @asynccontextmanager(装饰异步生成器),前者配合with,后者配合async with
                4. 返回值yield 资源 是可选的,如果上下文不需要返回资源,直接yield即可(async with 管理器: 不带as)。

                手动实现异步上下文管理器的方式重写上面的「异步文件管理器」,对比代码量:

                import asyncio
                # 手动实现异步上下文管理器(需实现__aenter__/__aexit__)
                class AsyncOpen:
                    def __init__(self, file_path, mode="r"):
                        self.file_path = file_path
                        self.mode = mode
                        self.file = None
                    # 异步进入方法
                    async def __aenter__(self):
                        print("进入上下文:异步打开文件")
                        self.file = open(self.file_path, self.mode, encoding="utf-8")
                        return self.file
                    # 异步退出方法(exc_type/val/tb 接收异常信息)
                    async def __aexit__(self, exc_type, exc_val, exc_tb):
                        print("退出上下文:异步关闭文件")
                        if self.file:
                            self.file.close()
                # 调用方式不变
                async def read_file():
                    async with AsyncOpen("test.txt", "w") as f:
                        f.write("手动实现异步上下文管理器")
                asyncio.run(read_file())

                可以明显看到:手动实现需要写大量模板代码,而@asynccontextmanager用一个异步生成器就搞定,代码简洁数倍。

                1. @asynccontextmanager 是 Python contextlib 模块提供的异步上下文管理器装饰器,专为异步编程设计;
                2. 核心是将异步生成器函数转为异步上下文管理器,yield 前是「进入逻辑」,yield 后是「退出清理逻辑」;
                3. 必须配合 async with 使用,且生成器内要加 try...finally 保证资源必被清理;
                4. 对比手动实现 __aenter__/__aexit__,大幅简化异步资源管理的代码,是异步开发中管理临时资源(连接、文件、会话)的最佳实践之一;
                5. 同步场景用它的兄弟装饰器 @contextmanager(配合with,装饰普通生成器),异步场景用@asynccontextmanager(配合async with,装饰异步生成器)。

                到此这篇关于Python 中的异步上下文生成器 @asynccontextmanager的文章就介绍到这了,更多相关Python 异步上下文生成器 @asynccontextmanager内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

                您可能感兴趣的文章:

                • 基于Python开发一个小说图片PDF生成器
                • Python使用生成器轻松处理海量数据
                • Python可迭代对象、迭代器与生成器实现示例详解
                • python生成器函数yield与yield from使用
                • 使用Python构建智能BAT文件生成器的完美解决方案
                • Python实现中文数字与阿拉伯数字映射生成器(支持0-9999)的完整代码
                • Python实现安全密码生成器的示例代码

                站内搜索