← 返回题目列表

Python 中 async for 和 async with 的原理是什么?异步迭代器和异步上下文管理器怎么理解?

高频 困难 第 15 / 27 题 更新于 2026/07/27
async forasync with异步迭代器异步上下文管理器

简化版

async for 用来遍历异步迭代器,背后依赖 __aiter____anext__,每次取下一个元素可以异步等待;async with 用来管理异步资源,背后依赖 __aenter____aexit__,进入和退出资源时都可以执行异步操作。它们常用于异步流式读取、异步连接、异步事务等场景。

详细版

异步迭代:

async for row in async_cursor:
    handle(row)

适合数据库游标、消息流、分页 API、WebSocket 消息等“每次取数据都可能等待 I/O”的场景。

异步上下文管理:

async with session.get(url) as response:
    data = await response.text()

适合异步打开和关闭资源,例如网络连接、异步文件、连接池借还、事务提交回滚。

协议方法:

语法协议方法
async for__aiter____anext__
async with__aenter____aexit__

面试回答重点:普通 for / with 处理同步对象,async for / async with 处理进入、退出、取下一个元素都可能需要 await 的异步对象。

完整版教学

一、为什么需要 async for

普通迭代器的 next() 是同步返回的。如果数据来自网络、数据库或消息队列,获取下一条数据可能要等待 I/O。

如果用普通 for,等待过程可能阻塞事件循环。async for 允许“取下一项”这个动作本身是异步的。

例如异步分页:

async for item in client.iter_items():
    await process(item)

每次迭代背后都可能发一次请求,但等待时不会卡住整个事件循环。

二、异步迭代器协议

异步迭代器需要实现:

def __aiter__(self):
    return self

async def __anext__(self):
    ...

__anext__ 返回下一项;没有下一项时抛 StopAsyncIteration

简化示例:

class AsyncCounter:
    def __init__(self, n):
        self.n = n
        self.i = 0

    def __aiter__(self):
        return self

    async def __anext__(self):
        if self.i >= self.n:
            raise StopAsyncIteration
        await asyncio.sleep(0.1)
        self.i += 1
        return self.i

使用:

async for value in AsyncCounter(3):
    print(value)

三、异步生成器更常用

手写 __aiter__ / __anext__ 有点繁琐。实际开发里经常用异步生成器:

async def stream_pages():
    page = 1
    while True:
        data = await fetch_page(page)
        if not data:
            break
        for item in data:
            yield item
        page += 1

它可以被 async for 消费:

async for item in stream_pages():
    await process(item)

异步生成器适合表达流式数据,非常自然。

四、为什么需要 async with

普通 with 适合同步资源:

with open("a.txt") as f:
    ...

异步资源的进入和退出可能都需要等待。例如建立连接、发送关闭帧、释放连接回池、异步提交事务。

这时用:

async with get_connection() as conn:
    await conn.query(sql)

它能保证退出时执行异步清理逻辑。

五、异步上下文管理器协议

异步上下文管理器实现:

class AsyncResource:
    async def __aenter__(self):
        await self.open()
        return self

    async def __aexit__(self, exc_type, exc, tb):
        await self.close()

使用:

async with AsyncResource() as resource:
    await resource.do_work()

即使代码块内抛异常,__aexit__ 也会执行,适合资源清理。

六、常见应用场景

async for 常用于:

  • 异步数据库游标;
  • WebSocket 消息;
  • 消息队列消费;
  • 流式 HTTP 响应;
  • 分页接口封装。

async with 常用于:

  • 异步 HTTP session;
  • 数据库连接和事务;
  • 异步锁;
  • 连接池资源;
  • 异步文件对象。

七、常见误区与追问

语法依赖协议解决的问题
async for__aiter__ / __anext__迭代过程中允许异步等待
异步生成器async def + yield更简洁地生产异步数据流
async with__aenter__ / __aexit__资源进入和释放都可 await
同步 with__enter__ / __exit__不适合释放过程需要异步 I/O 的资源
  • 误区:async for 只是普通 for 的语法糖。 它每次取下一个元素时可以 await,适合网络流、分页拉取、异步队列等数据源。
  • 误区:异步迭代器的 anext 返回普通值就可以。 __anext__ 应返回 awaitable,最终产出下一个元素或抛出 StopAsyncIteration
  • 误区:async with 只是在 with 前面加 async。 它要求异步上下文管理器协议,进入和退出阶段都可能执行异步 I/O。
  • 追问:为什么数据库连接池常用 async with? 获取连接、开启事务、提交回滚、释放连接都可能涉及异步 I/O,async with 能把清理路径固定住。
  • 追问:异步生成器和手写异步迭代器怎么选? 只是逐步产出数据时优先异步生成器;需要复杂状态封装或可复用类时再手写协议。
  • 追问:async with 里发生异常会怎样? 异常会传给 __aexit__,它仍有机会执行异步清理;是否吞掉异常取决于 __aexit__ 的返回值。

记忆钩子:async for 解决“取下一个也要等”,async with 解决“进入和退出资源也要等”。

八、加强记忆

async for 解决“取下一项也要 await”的问题,背后是 __aiter__ / __anext__async with 解决“进入和退出资源也要 await”的问题,背后是 __aenter__ / __aexit__。只要迭代、打开、关闭、提交、释放这些动作涉及异步 I/O,就该想到异步协议。