一、async with client 是什么?

async with 是 Python 3.5+ 引入的异步上下文管理器语法,专门用于处理异步操作中的资源申请与释放(比如异步网络连接的建立 / 关闭、异步文件的打开 / 关闭、异步锁的获取 / 释放等)。

对应到普通的同步上下文管理器(with ... as ...),async with 是其异步版本,核心作用是:

  1. 在进入代码块时,自动执行异步的初始化逻辑(比如建立异步连接);
  2. 在退出代码块时(即使发生异常),自动执行异步的清理逻辑(比如关闭异步连接);
  3. 避免手动编写重复的资源管理代码,提升代码的健壮性。

二、为什么 client 能被 async with 调用(异步上下文管理器的原理)

一个对象能被 async with 使用,前提是这个对象实现了异步上下文管理器的两个核心方法

方法 作用 同步版本对应
__aenter__() 异步进入上下文时执行(比如初始化异步连接),返回值可被 as 接收 __enter__()
__aexit__() 异步退出上下文时执行(比如关闭异步连接),处理异常、清理资源 __exit__()

这两个方法必须是协程函数(用 async def 定义),这是异步上下文管理器的关键要求。

示例:手动实现一个支持 async withStorageClient

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 连接...

从结果可以看到:

  1. 进入 async with 块时,自动执行 __aenter__()(建立连接);
  2. 执行块内的业务逻辑;
  3. 退出块时,自动执行 __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()

这里的 clientStorageClient 实例,实现了 __aenter____aexit__ 协程方法:

  1. __aenter__:异步初始化存储连接(比如建立和阿里云 OSS 的异步网络连接);
  2. __aexit__:异步释放存储连接(比如关闭网络连接、释放资源);
  3. 即使 check_connection() 抛出异常,__aexit__ 也会被执行,确保资源不会泄露。

总结

  1. async with client异步上下文管理器的调用语法,用于异步资源的自动管理;
  2. client 能被调用的前提是实现了 __aenter__/__aexit__ 两个异步方法(协程函数);
  3. async with 本身是语法糖,但其内部执行的核心方法是协程,依赖事件循环运行;
  4. 核心价值:简化异步资源的申请 / 释放,避免手动处理异常导致的资源泄露。

文章作者: Mealsee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Mealsee !
  目录