在这一篇中,我们将深入探讨 Python 生成器函数的工作原理,结合 asyncio 库实现异步编程。通过分析生成器函数的机制,我们将了解它们如何在 Python 中实现异步 I/O 操作,并通过实践示例深入解析。
生成器函数是一种特殊类型的函数,它使用 yield 语句返回一个迭代器(iterator)。与普通的返回值不同,生成器函数返回的是一个 生成器对象,它可以在每次调用时逐个产生结果,而不会一次性将所有结果返回。
def simple_generator(): yield 1 yield 2 yield 3gen = simple_generator()print(next(gen)) # 输出 1print(next(gen)) # 输出 2print(next(gen)) # 输出 3php160 Bytes© 菜鸟-创作你的创作生成器与普通函数的区别在于,生成器函数在每次 yield 时暂停执行,直到下一次调用 next() 时继续执行。
当调用生成器函数时,它并不会立即执行函数体,而是返回一个生成器对象。生成器对象可以使用 next() 函数或 for 循环逐个“拉取”值。
yield,生成器函数就会暂停,返回一个值。next() 时,生成器函数会从上次 yield 停止的地方继续执行,直到遇到下一个 yield。yield 语句可执行时,它会抛出 StopIteration 异常,表示生成器已完成。def countdown(n): while n > 0: yield n n -= 1 print("Finished!")gen = countdown(5)for num in gen: print(num)php139 Bytes© 菜鸟-创作你的创作输出:
54321Finished!php19 Bytes© 菜鸟-创作你的创作在 Python 中,异步编程通常是通过 asyncio 库实现的,它的核心是事件循环和协程(coroutines)。生成器和协程有很多相似之处,它们都可以挂起和恢复执行。因此,理解生成器的工作机制有助于我们理解异步编程的底层实现。
asyncio 和异步编程概述异步编程的关键是 事件循环,它负责调度和执行异步任务。通过将任务标记为“挂起”,异步程序可以在等待 I/O 操作时执行其他任务,这样可以避免阻塞和浪费时间。
Python 的异步编程是通过 协程(coroutines)和 asyncio 事件循环来实现的,asyncio 提供了用于并发执行 I/O 操作的工具。
async def 声明。import asyncioasync def hello_world(): print("Hello") await asyncio.sleep(1) print("World")# 事件循环运行协程asyncio.run(hello_world())php144 Bytes© 菜鸟-创作你的创作Hello(等待 1 秒)Worldphp20 Bytes© 菜鸟-创作你的创作在上面的例子中,await 会暂停协程的执行,直到 asyncio.sleep(1) 完成,然后继续执行。这是异步编程的核心:在等待某个操作(如网络请求、磁盘 I/O 等)时,可以去执行其他任务。
在旧版的 Python 中,asyncio 事件循环是基于生成器实现的,通过使用 yield 控制异步任务的挂起和恢复。通过这种方式,生成器函数可以像协程一样异步执行。
import asyncio# 自定义事件循环def my_sleep(seconds): print(f"Sleeping for {seconds} seconds...") yield asyncio.sleep(seconds) print(f"Woke up after {seconds} seconds")async def main(): # 异步调用 await asyncio.gather( my_sleep(2), my_sleep(3) )# 事件循环执行asyncio.run(main())php308 Bytes© 菜鸟-创作你的创作注意:在 Python 3.5 之后,async def 和 await 语法被引入,提供了更加简洁和高效的方式来实现异步编程。yield 不再用于异步操作,但它仍然是理解协程工作原理的关键。
asyncio 的结合:异步生成器在 Python 3.6 及之后的版本,Python 引入了 异步生成器(async def 与 yield 的结合)。这种方式允许你在生成器中使用异步操作(如 await),从而实现更加复杂的异步行为。
异步生成器函数与普通生成器函数类似,只是它们是异步的,使用 async def 声明,并且 yield 语句会在协程中等待某些异步操作。
import asyncioasync def async_countdown(n): while n > 0: print(f"Counting down: {n}") await asyncio.sleep(1) # 模拟异步操作 yield n n -= 1 print("Finished!")async def main(): async for number in async_countdown(5): print(f"Got number: {number}")# 启动事件循环asyncio.run(main())php326 Bytes© 菜鸟-创作你的创作输出:
Counting down: 5(等待 1 秒)Got number: 5Counting down: 4(等待 1 秒)Got number: 4Counting down: 3(等待 1 秒)Got number: 3Counting down: 2(等待 1 秒)Got number: 2Counting down: 1(等待 1 秒)Got number: 1Finished!php209 Bytes© 菜鸟-创作你的创作async def async_countdown(n) 是一个异步生成器。await asyncio.sleep(1) 是一个异步操作,模拟耗时任务。async for number in async_countdown(5) 用来异步迭代生成器的值。asyncio 提供了一个事件循环,负责调度和执行异步任务。其底层实现与生成器类似,使用了协程和生成器的概念。事件循环会负责监控所有正在运行的协程,直到它们完成。
await 时,事件循环会挂起该任务,直到 await 的结果返回。import asyncioasync def task_1(): print("Task 1 started") await asyncio.sleep(1) print("Task 1 finished")async def task_2(): print("Task 2 started") await asyncio.sleep(2) print("Task 2 finished")async def main(): task1 = asyncio.create_task(task_1()) task2 = asyncio.create_task(task_2()) await task1 await task2# 启动事件循环asyncio.run(main())php390 Bytes© 菜鸟-创作你的创作Task 1 startedTask 2 startedTask 1 finishedTask 2 finishedphp61 Bytes© 菜鸟-创作你的创作在这里,两个任务是并发执行的,task_1 的执行时间较短,而 task_2 需要 2 秒。事件循环调度任务时,确保 task_1 和 task_2 在等待时不会阻塞其他任务。
yield 按需生成数据,可以逐步生成和返回数据,而不是一次性返回所有数据。asyncio 库和协程实现,避免阻塞,提高程序的执行效率,特别是在 I/O 密集型任务中。asyncio 的核心,通过调度和执行协程,保证异步任务的执行顺序。通过理解生成器的原理和异步编程的机制,开发者可以有效
地管理 I/O 操作,提升程序的性能。https://www.52runoob.com/archives/3457
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。