02_async_await基础

本课学习Python异步编程的两个核心关键字:async和await。async用来定义可以”暂停”的函数,await用来等待异步操作(等待时不阻塞程序)。就像餐厅点餐,你点完餐拿到号码牌(async),然后去找座位玩手机(await),叫号时再去取餐。我们将通过咖啡机、倒计时等实用示例,让你完全掌握这两个关键字,并能编写自己的异步程序。


📖 课程目标

  • 理解 asyncawait 关键字的作用
  • 掌握协程(Coroutine)的概念
  • 学会运行异步函数的多种方法
  • 编写第一个实用的异步程序

🔑 核心概念

1. async:定义异步函数

async 关键字用来定义一个异步函数(也叫协程函数):

1
2
3
4
5
6
7
# 普通函数
def 普通函数():
return "我是普通函数"

# 异步函数(协程函数)
async def 异步函数():
return "我是异步函数"

关键区别

  • 普通函数:调用后立即执行,返回结果
  • 异步函数:调用后返回一个”协程对象”,需要用特殊方式运行

2. await:等待异步操作

await 关键字用来等待一个异步操作完成:

1
2
3
4
5
async def 下载文件():
print("开始下载...")
await asyncio.sleep(3) # 等待3秒,但不阻塞其他任务
print("下载完成!")
return "文件内容"

await 的特点

  • 只能在 async 函数内使用
  • 等待时,程序可以去执行其他任务
  • 等待完成后,继续执行后面的代码

🎪 生活类比:餐厅点餐

想象你在餐厅点餐:

普通函数(同步)

1
2
3
4
5
6
你:老板,来份炒饭!
老板:好的![开始炒饭]
你:[站在那里等待...]
老板:[炒了10分钟]
老板:炒饭好了!
你:[拿到炒饭,离开]

异步函数(async/await)

1
2
3
4
5
6
你:老板,来份炒饭!(async def 点餐)
老板:好的,请稍等![给你一个号码牌]
你:[拿着号码牌,去找座位、玩手机] (await)
老板:[炒饭中...]
老板:36号,您的炒饭好了!
你:[听到叫号,去取餐]

关键点

  • async def = 点餐(发起请求)
  • await = 等待叫号(等待时可以做其他事)
  • 号码牌 = 协程对象

📝 基础语法详解

语法1:定义异步函数

1
2
3
4
5
6
7
8
# 格式
async def 函数名(参数):
# 函数体
return 结果

# 示例
async def 问候(名字: str) -> str:
return f"你好,{名字}!"

语法2:调用异步函数

1
2
3
4
5
6
7
8
9
10
11
# ❌ 错误方式:直接调用
结果 = 问候("小明") # 这样只会得到一个协程对象,不会执行

# ✅ 正确方式1:使用 await(在另一个异步函数中)
async def main():
结果 = await 问候("小明") # 会真正执行
print(结果)

# ✅ 正确方式2:使用 asyncio.run(在主程序中)
import asyncio
asyncio.run(main())

语法3:使用 await 等待

1
2
3
4
5
6
7
8
9
10
11
12
13
async def 做事情():
print("步骤1:开始")

# 等待异步操作
await asyncio.sleep(2) # 等待2秒

print("步骤2:继续")

# 等待另一个异步函数
结果 = await 其他异步函数()

print("步骤3:完成")
return 结果

🔍 协程(Coroutine)是什么?

定义

协程是一个可以”暂停”和”恢复”的函数。

生活类比:看书

1
2
3
4
5
6
7
普通函数 = 一口气读完一本书
开始 → 读完 → 结束

协程 = 可以随时放下,随时继续的书
开始 → 读一会儿 → 暂停(去做其他事)
→ 继续读 → 暂停(去做其他事)
→ 继续读 → 结束

代码示例

1
2
3
4
5
6
7
8
async def 协程示例():
print("第1步:开始执行")

await asyncio.sleep(1) # 暂停点1
print("第2步:继续执行")

await asyncio.sleep(1) # 暂停点2
print("第3步:完成")

执行流程

  1. 执行到第一个 await → 暂停 → 去执行其他任务
  2. 1秒后 → 恢复 → 继续执行
  3. 执行到第二个 await → 暂停 → 去执行其他任务
  4. 1秒后 → 恢复 → 完成

🚀 运行异步函数的方法

方法1:asyncio.run()(最常用)

1
2
3
4
5
6
7
8
9
10
import asyncio

async def main():
print("Hello")
await asyncio.sleep(1)
print("World")

# 在主程序中运行
if __name__ == "__main__":
asyncio.run(main())

特点

  • ✅ 最简单、最推荐的方式
  • ✅ 自动创建和关闭事件循环
  • ✅ Python 3.7+ 可用

方法2:await(在异步函数内)

1
2
3
4
5
6
7
8
9
10
11
12
async def 任务A():
print("任务A")
await asyncio.sleep(1)

async def 任务B():
print("任务B")
await 任务A() # 在异步函数内调用另一个异步函数

async def main():
await 任务B()

asyncio.run(main())

方法3:asyncio.create_task()(创建后台任务)

1
2
3
4
5
6
7
8
async def main():
# 创建任务,立即开始执行
任务 = asyncio.create_task(异步函数())

# 做其他事情...

# 等待任务完成
结果 = await 任务

方法4:asyncio.gather()(同时运行多个)

1
2
3
4
5
6
7
async def main():
# 同时运行多个异步函数
结果 = await asyncio.gather(
异步函数1(),
异步函数2(),
异步函数3()
)

💡 常见错误和解决方案

错误1:忘记使用 await

1
2
3
4
5
6
7
8
9
# ❌ 错误
async def main():
结果 = 异步函数() # 只得到协程对象,不会执行
print(结果) # 输出:<coroutine object ...>

# ✅ 正确
async def main():
结果 = await 异步函数() # 真正执行
print(结果)

错误2:在普通函数中使用 await

1
2
3
4
5
6
7
# ❌ 错误
def 普通函数():
await asyncio.sleep(1) # 语法错误!

# ✅ 正确
async def 异步函数():
await asyncio.sleep(1) # 正确

错误3:忘记使用 asyncio.run()

1
2
3
4
5
6
7
8
9
10
11
12
13
# ❌ 错误
async def main():
print("Hello")

main() # 只创建协程对象,不会执行

# ✅ 正确
import asyncio

async def main():
print("Hello")

asyncio.run(main()) # 真正执行

🎯 实战练习:智能咖啡机

让我们用异步编程模拟一个智能咖啡机!

需求

  1. 咖啡机可以同时处理多个订单
  2. 每杯咖啡需要不同的制作时间
  3. 显示制作进度

代码框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
async def 制作咖啡(订单号: int, 咖啡类型: str, 制作时间: int) -> str:
"""制作一杯咖啡"""
print(f"☕ 订单{订单号}:开始制作{咖啡类型}...")

# 模拟制作过程
await asyncio.sleep(制作时间)

print(f"✅ 订单{订单号}{咖啡类型}制作完成!")
return f"{咖啡类型}(订单{订单号})"

async def 咖啡店营业():
"""同时处理多个订单"""
订单列表 = [
(1, "美式咖啡", 3),
(2, "拿铁", 4),
(3, "卡布奇诺", 5),
(4, "摩卡", 4),
]

# 同时制作所有咖啡
任务列表 = [制作咖啡(订单号, 类型, 时间)
for 订单号, 类型, 时间 in 订单列表]

结果 = await asyncio.gather(*任务列表)

print(f"\n🎉 今日完成订单:{len(结果)}杯")
for 咖啡 in 结果:
print(f" - {咖啡}")

完整代码在 02_examples.py 中!


📊 同步 vs 异步对比

场景:制作4杯咖啡

方式制作时间总耗时客户体验
同步3+4+5+4=16秒16秒😤 等太久
异步max(3,4,5,4)=5秒5秒😊 很快

📝 本课小结

核心语法

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 定义异步函数
async def 函数名():
pass

# 2. 等待异步操作
await 异步函数()

# 3. 运行异步函数
asyncio.run(main())

# 4. 同时运行多个
await asyncio.gather(函数1(), 函数2())

记住这些要点

  1. async def = 定义可以暂停的函数
  2. await = 等待时可以做其他事
  3. 协程 = 可以暂停和恢复的函数
  4. asyncio.run() = 启动异步程序的入口

使用场景

  • ✅ 网络请求(下载、API调用)
  • ✅ 文件读写(大文件、批量操作)
  • ✅ 数据库查询(批量查询)
  • ✅ 任何需要”等待”的操作

🎯 下一步

  1. 运行 02_examples.py 查看完整示例
  2. 修改代码,尝试不同的参数
  3. 完成课后练习题
  4. 准备学习第3课:并发执行多个任务

💪 课后练习

练习1:异步倒计时

编写一个异步倒计时函数,从10倒数到1。

1
2
3
async def 倒计时(秒数: int):
# 你的代码
pass

练习2:异步问候

编写一个异步函数,向3个人依次问候,每次问候间隔1秒。

1
2
3
async def 问候多人(名字列表: List[str]):
# 你的代码
pass

练习3:模拟下载

编写一个异步函数,模拟同时下载3个文件,每个文件大小不同。

1
2
3
async def 下载文件(文件名: str, 大小MB: int):
# 你的代码(假设每MB需要0.5秒)
pass

答案在 练习答案.py 中! 先自己尝试,再看答案哦!😊

02_examples.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
"""
第2课示例代码:async/await 基础语法

运行方式:
python 02_examples.py
"""

import asyncio
import time
from typing import List


# ============================================
# 示例1:最简单的异步函数
# ============================================

async def 简单问候(名字: str) -> str:
"""最简单的异步函数"""
print(f"你好,{名字}!")
await asyncio.sleep(1) # 等待1秒
print(f"很高兴见到你,{名字}!")
return f"问候完成:{名字}"


async def 示例1_基础语法() -> None:
"""示例1:演示基础的 async/await 语法"""
print("\n" + "=" * 50)
print("📚 示例1:最简单的异步函数")
print("=" * 50)

# 调用异步函数
结果 = await 简单问候("小明")
print(f"返回值:{结果}")

print("\n💡 关键点:")
print(" 1. 用 async def 定义异步函数")
print(" 2. 用 await 等待异步操作")
print(" 3. await 只能在 async 函数内使用")


# ============================================
# 示例2:协程的暂停和恢复
# ============================================

async def 展示暂停恢复() -> None:
"""展示协程如何暂停和恢复"""
print("\n" + "=" * 50)
print("📚 示例2:协程的暂停和恢复")
print("=" * 50)

print("▶️ 步骤1:开始执行")

print("⏸️ 暂停1秒(这时可以执行其他任务)...")
await asyncio.sleep(1)

print("▶️ 步骤2:恢复执行")

print("⏸️ 暂停1秒(这时可以执行其他任务)...")
await asyncio.sleep(1)

print("▶️ 步骤3:继续执行")

print("⏸️ 暂停1秒(这时可以执行其他任务)...")
await asyncio.sleep(1)

print("✅ 步骤4:完成!")

print("\n💡 关键点:")
print(" 协程在每个 await 处暂停,等待完成后恢复")
print(" 暂停期间,程序可以去执行其他协程")


# ============================================
# 示例3:多个异步函数协作
# ============================================

async def 烧水() -> str:
"""烧水"""
print("🔥 开始烧水...")
await asyncio.sleep(3)
print("💧 水烧开了!")
return "开水"


async def 泡茶(水: str) -> str:
"""泡茶"""
print(f"🍵 用{水}泡茶...")
await asyncio.sleep(2)
print("☕ 茶泡好了!")
return "一杯茶"


async def 示例3_函数协作() -> None:
"""示例3:多个异步函数协作"""
print("\n" + "=" * 50)
print("📚 示例3:多个异步函数协作")
print("=" * 50)

开始时间 = time.time()

# 先烧水,再泡茶(顺序执行)
水 = await 烧水()
茶 = await 泡茶(水)

总耗时 = time.time() - 开始时间

print(f"\n✅ 完成!得到:{茶}")
print(f"⏱️ 总耗时:{总耗时:.1f}秒")

print("\n💡 关键点:")
print(" 使用 await 可以在异步函数之间传递数据")
print(" 这里是顺序执行:先烧水,再泡茶")


# ============================================
# 示例4:实战 - 智能咖啡机
# ============================================

async def 制作咖啡(订单号: int, 咖啡类型: str, 制作时间: int) -> str:
"""制作一杯咖啡"""
print(f"☕ 订单{订单号}:开始制作{咖啡类型}...")

# 模拟制作过程(分步显示进度)
for 进度 in range(1, 4):
await asyncio.sleep(制作时间 / 3)
print(f" 订单{订单号}:制作进度 {进度}/3")

print(f"✅ 订单{订单号}{咖啡类型}制作完成!")
return f"{咖啡类型}(订单{订单号})"


async def 咖啡店营业_顺序() -> None:
"""咖啡店营业 - 顺序处理订单(同步方式)"""
print("\n" + "=" * 50)
print("📚 示例4A:咖啡店 - 顺序处理订单")
print("=" * 50)

开始时间 = time.time()

订单列表 = [
(1, "美式咖啡", 3),
(2, "拿铁", 4),
(3, "卡布奇诺", 5),
]

完成的咖啡 = []

# 一个一个处理订单
for 订单号, 类型, 时间 in 订单列表:
咖啡 = await 制作咖啡(订单号, 类型, 时间)
完成的咖啡.append(咖啡)

总耗时 = time.time() - 开始时间

print(f"\n🎉 完成订单:{len(完成的咖啡)}杯")
for 咖啡 in 完成的咖啡:
print(f" - {咖啡}")
print(f"⏱️ 总耗时:{总耗时:.1f}秒")

print("\n💭 分析:顺序处理,效率较低...")


async def 咖啡店营业_并发() -> None:
"""咖啡店营业 - 并发处理订单(异步方式)"""
print("\n" + "=" * 50)
print("📚 示例4B:咖啡店 - 并发处理订单")
print("=" * 50)

开始时间 = time.time()

订单列表 = [
(1, "美式咖啡", 3),
(2, "拿铁", 4),
(3, "卡布奇诺", 5),
]

# 同时处理所有订单
任务列表 = [制作咖啡(订单号, 类型, 时间)
for 订单号, 类型, 时间 in 订单列表]

完成的咖啡 = await asyncio.gather(*任务列表)

总耗时 = time.time() - 开始时间

print(f"\n🎉 完成订单:{len(完成的咖啡)}杯")
for 咖啡 in 完成的咖啡:
print(f" - {咖啡}")
print(f"⏱️ 总耗时:{总耗时:.1f}秒")

print("\n💭 分析:并发处理,效率大大提高!")


# ============================================
# 示例5:常见错误演示
# ============================================

async def 示例5_常见错误() -> None:
"""示例5:演示常见错误和正确做法"""
print("\n" + "=" * 50)
print("📚 示例5:常见错误和正确做法")
print("=" * 50)

# 错误1:忘记使用 await
print("\n❌ 错误1:忘记使用 await")
print("代码:结果 = 简单问候('小红')")
结果错误 = 简单问候("小红") # 只得到协程对象
print(f"结果:{结果错误}")
print("说明:这样只会得到协程对象,不会真正执行!")

# 正确做法
print("\n✅ 正确做法:使用 await")
print("代码:结果 = await 简单问候('小红')")
结果正确 = await 简单问候("小红")
print(f"结果:{结果正确}")
print("说明:使用 await 才会真正执行!")

print("\n💡 记住:")
print(" 调用异步函数时,一定要用 await")
print(" 否则只会得到协程对象,不会执行")


# ============================================
# 示例6:实用工具函数
# ============================================

async def 异步倒计时(秒数: int) -> None:
"""异步倒计时"""
print(f"\n⏰ 倒计时 {秒数} 秒...")
for i in range(秒数, 0, -1):
print(f" {i}...")
await asyncio.sleep(1)
print(" 🎉 时间到!")


async def 异步进度条(任务名: str, 总步骤: int, 每步耗时: float) -> None:
"""异步进度条"""
print(f"\n📊 {任务名}")
for 步骤 in range(1, 总步骤 + 1):
进度百分比 = (步骤 / 总步骤) * 100
进度条 = "█" * 步骤 + "░" * (总步骤 - 步骤)
print(f" [{进度条}] {进度百分比:.0f}%", end="\r")
await asyncio.sleep(每步耗时)
print(f" [{'█' * 总步骤}] 100% ✅")


async def 示例6_实用工具() -> None:
"""示例6:实用的异步工具函数"""
print("\n" + "=" * 50)
print("📚 示例6:实用的异步工具函数")
print("=" * 50)

# 同时运行倒计时和进度条
await asyncio.gather(
异步倒计时(5),
异步进度条("下载文件", 10, 0.5)
)

print("\n💡 关键点:")
print(" 可以创建各种实用的异步工具函数")
print(" 用 asyncio.gather 可以同时运行多个")


# ============================================
# 主程序
# ============================================

async def main() -> None:
"""主程序:运行所有示例"""
print("🎓 第2课:async/await 基础语法")
print("=" * 50)

# 运行所有示例
await 示例1_基础语法()
await 展示暂停恢复()
await 示例3_函数协作()
await 咖啡店营业_顺序()
await 咖啡店营业_并发()
await 示例5_常见错误()
await 示例6_实用工具()

# 总结
print("\n" + "=" * 50)
print("🎉 第2课完成!")
print("=" * 50)
print("""
📚 你学到了什么?
1. async def - 定义异步函数
2. await - 等待异步操作(可以暂停)
3. asyncio.run() - 运行异步程序
4. asyncio.gather() - 同时运行多个任务
5. 协程可以暂停和恢复

🎯 核心语法:
async def 函数名(): # 定义异步函数
结果 = await 异步操作() # 等待异步操作
return 结果

asyncio.run(函数名()) # 运行异步函数

💪 动手练习:
1. 修改咖啡制作时间,观察效果
2. 增加更多咖啡订单
3. 创建自己的异步函数
4. 完成课后练习题

🎯 下一步:
学习更多并发控制技巧(第3课)
""")


if __name__ == "__main__":
# 运行主程序
asyncio.run(main())