[Python3高阶编程] - 异步编程深度学习指南一(补充2):深入理解关键词 await
·
一、await 的核心作用
await的作用是:暂停当前协程的执行,将控制权交还给事件循环(event loop),直到等待的对象“完成”后再恢复执行,并获取其结果。
类比理解:
想象你在排队买咖啡:
- step1. 你点单后(发起 I/O 请求),店员说“稍等,正在做”
- step2. 你不会傻站着(阻塞),而是去刷手机(事件循环执行其他任务)
- step3. 咖啡好了(I/O 完成),店员叫你(事件循环唤醒你)
- step4. 你拿到咖啡(获取结果),继续走路
await 就是那个“点单(step1) + 等待叫号(step3)”的动作。
二、await 后面能接什么?——必须是 “awaitable” 对象
await 不能接任意对象,只能接实现了 __await__ 方法的对象,即 “awaitable”。
Python 中主要有三类 awaitable 对象:
| 类型 | 说明 | 示例 |
|---|---|---|
| 1. 协程(Coroutine) | 由 async def 定义的函数调用返回 | fetch_data() |
| 2. Task | 对协程的封装,由事件循环调度 | asyncio.create_task(fetch_data()) |
| 3. Future | 底层异步操作的占位符(通常由库内部使用) | loop.run_in_executor(...) 返回 |
✅ 重点:
async def func(): ...定义的是协程函数- 调用
func()返回的是协程对象(coroutine object)- 这个协程对象就是 awaitable 的!
三、详细示例解析
示例 1:await 协程对象(最常见)
import asyncio
async def fetch_data():
await asyncio.sleep(1) # 模拟异步 I/O
return "Data from server"
async def main():
# fetch_data() 返回一个协程对象
coro = fetch_data()
print(type(coro)) # <class 'coroutine'>
# await 它,会执行并返回结果
result = await coro
print(result) # "Data from server"
asyncio.run(main())
✅ 过程:
fetch_data()创建协程对象(未执行!)await coro→ 事件循环开始执行该协程- 遇到
await asyncio.sleep(1)→ 暂停,让出控制权 - 1 秒后,sleep 完成 → 恢复执行 → 返回
"Data from server" result得到该值
示例 2:await Task(推荐用于并发)
async def main():
# 创建 Task(立即被调度)
task = asyncio.create_task(fetch_data())
# 可以做其他事...
print("Doing other work...")
# 等待 Task 完成并获取结果
result = await task
print(result)
💡 Task vs 协程对象:
- 直接
await fetch_data():顺序执行task = create_task(...); await task:可与其他任务并发(虽然这里只等一个)
示例 3:await 多个任务(并发)
async def main():
# 同时启动多个任务
task1 = asyncio.create_task(fetch_data())
task2 = asyncio.create_task(fetch_data())
# 等待全部完成
r1 = await task1
r2 = await task2
# 或
r1, r2 = await asyncio.gather(task1, task2)
四、await 如何返回结果?
✅ 规则很简单:
await some_awaitable的值 = 该 awaitable 最终return的值
async def add(a, b):
return a + b # ← 返回值
async def main():
result = await add(2, 3) # result = 5
❌ 如果协程抛出异常:
async def bad():
raise ValueError("Oops!")
async def main():
try:
await bad()
except ValueError as e:
print(e) # "Oops!" —— 异常会被 re-raise
⚠️ 重要:
await不仅传递结果,也传递异常!
五、常见误区澄清
❌ 误区 1:await 只能接 async def 函数调用
✅ 正确:可以接任何 awaitable(Task、Future、自定义 awaitable)
❌ 误区 2:await func() 会“启动”函数
✅ 正确:func() 已经创建了协程对象,await 是“执行并等待完成”
❌ 误区 3:await 是“阻塞”的
✅ 正确:对当前协程是“暂停”,但对整个程序是非阻塞的!
事件循环会去执行其他协程。
六、自定义 awaitable 对象(高级)
你也可以让自己的类支持 await:
class MyAwaitable:
def __await__(self):
# 必须返回一个 generator 或 awaitable
yield from asyncio.sleep(1).__await__()
return "Custom result"
async def main():
result = await MyAwaitable()
print(result) # "Custom result"
💡 实际开发中极少需要,但理解这点有助于掌握原理。
七、总结:await 的本质
| 问题 | 答案 |
|---|---|
await 的作用? | 暂停当前协程,交出控制权,等待异步操作完成 |
| 后面接什么? | 必须是 awaitable 对象(协程、Task、Future) |
| 如何返回结果? | 返回该 awaitable 的 return 值(或抛出异常) |
| 是否阻塞? | 不阻塞事件循环,只暂停当前协程 |
必须在 async def 中使用? | ✅ 是的!否则语法错误 |
💡 终极口诀:
“await等的是‘未来’,拿的是‘结果’,放的是‘控制权’。”
掌握这一点,你就真正理解了 Python 异步编程的精髓!
更多推荐
所有评论(0)