一、async with client 是什么?
async with 是 Python 3.5+ 引入的异步上下文管理器语法,专门用于处理异步操作中的资源申请与释放(比如异步网络连接的建立 / 关闭、异步文件的打开 / 关闭、异步锁的获取 / 释放等)。
对应到普通的同步上下文管理器(with ... as ...),async with 是其异步版本,核心作用是:
- 在进入代码块时,自动执行异步的初始化逻辑(比如建立异步连接);
- 在退出代码块时(即使发生异常),自动执行异步的清理逻辑(比如关闭异步连接);
- 避免手动编写重复的资源管理代码,提升代码的健壮性。
二、为什么 client 能被 async with 调用(异步上下文管理器的原理)
一个对象能被 async with 使用,前提是这个对象实现了异步上下文管理器的两个核心方法:
| 方法 | 作用 | 同步版本对应 |
|---|---|---|
__aenter__() |
异步进入上下文时执行(比如初始化异步连接),返回值可被 as 接收 |
__enter__() |
__aexit__() |
异步退出上下文时执行(比如关闭异步连接),处理异常、清理资源 | __exit__() |
这两个方法必须是协程函数(用 async def 定义),这是异步上下文管理器的关键要求。
示例:手动实现一个支持 async with 的 StorageClient
import asyncio
class StorageClient:
def __init__(self, storage_name):
self.storage_name = storage_name
# 异步进入上下文:协程函数,初始化资源
async def __aenter__(self):
print(f"[异步] 建立 {self.storage_name} 连接...")
# 模拟异步连接建立(比如网络IO)
await asyncio.sleep(0.5)
return self # 可以被 async with client as xxx 中的 xxx 接收
# 异步退出上下文:协程函数,清理资源
async def __aexit__(self, exc_type, exc_val, exc_tb):
# exc_type/exc_val/exc_tb 用于接收异常信息(如果有)
print(f"[异步] 关闭 {self.storage_name} 连接...")
# 模拟异步连接关闭
await asyncio.sleep(0.5)
# 如果返回 True,会抑制异常;返回 False/None,会抛出异常
return False
# 测试 async with
async def main():
async with StorageClient("阿里云OSS") as client:
print(f"正在使用 {client.storage_name} 处理数据...")
# 模拟业务逻辑(异步操作)
await asyncio.sleep(1)
asyncio.run(main())
输出结果:
[异步] 建立 阿里云OSS 连接...
正在使用 阿里云OSS 处理数据...
[异步] 关闭 阿里云OSS 连接...
从结果可以看到:
- 进入
async with块时,自动执行__aenter__()(建立连接); - 执行块内的业务逻辑;
- 退出块时,自动执行
__aexit__()(关闭连接)。
三、它是协程吗?(拆解问题)
1. async with 本身不是协程,是语法糖
async with 是 Python 提供的语法结构,用于简化异步上下文管理器的调用,它本身不是协程函数,也不能被 await。
2. 上下文管理器的核心方法(__aenter__/__aexit__)是协程
这两个方法必须用 async def 定义,属于协程函数,执行时需要依赖事件循环(可以被 await)。
3. async with 内部会自动处理协程的执行
当你写 async with client: 时,Python 解释器会在底层做这些事(伪代码):
# 伪代码:async with 的底层逻辑
# 1. 调用 __aenter__ 协程,并用 await 执行
await client.__aenter__()
try:
# 执行 async with 块内的代码
pass
finally:
# 2. 调用 __aexit__ 协程,并用 await 执行
await client.__aexit__(exc_type, exc_val, exc_tb)
四、和同步上下文管理器的对比
| 特性 | 同步上下文管理器(with) |
异步上下文管理器(async with) |
|---|---|---|
| 核心方法 | __enter__()(普通函数)、__exit__()(普通函数) |
__aenter__()(协程)、__aexit__()(协程) |
| 执行环境 | 同步代码(主线程 / 普通线程) | 异步代码(事件循环中) |
| 适用场景 | 同步资源操作(如同步文件读写) | 异步资源操作(如异步网络连接) |
五、回到之前的代码:async with client 的意义
在之前的存储连接测试代码中:
async with client:
return await client.check_connection()
这里的 client 是 StorageClient 实例,实现了 __aenter__ 和 __aexit__ 协程方法:
__aenter__:异步初始化存储连接(比如建立和阿里云 OSS 的异步网络连接);__aexit__:异步释放存储连接(比如关闭网络连接、释放资源);- 即使
check_connection()抛出异常,__aexit__也会被执行,确保资源不会泄露。
总结
async with client是异步上下文管理器的调用语法,用于异步资源的自动管理;client能被调用的前提是实现了__aenter__/__aexit__两个异步方法(协程函数);async with本身是语法糖,但其内部执行的核心方法是协程,依赖事件循环运行;- 核心价值:简化异步资源的申请 / 释放,避免手动处理异常导致的资源泄露。