python引入io_Python异步IO实现全过程(2)

6a0454a1d434c41fabbc6f4a59463e83.png

生成器中异步IO的起源

之前,你看到了基于生成器的旧式风格的协程示例,虽然它已经被更为明确的原生协程所取代,但是还是值得回顾一下:

可以实验一下,如果你只纯粹的调用 py34_core() 或者 py35_core() 而不使用 await, 或者不调用 asyncio.run() 方法或其他高级函数,运行结果会怎样?单独调用一个协程会返回一个协程对象:

f147c3b0b9eb2e99edf00a032867e1e6.png

这表面上看起来有些无趣,协程调用的结果是一个协程对象。

一个小测验:Python还有哪些功能和这个比较像?(Python的什么功能是自身被调用的时候实际却“没做什么”?)

希望你能想到生成器作为答案,因为协程是在生成器之上做了一些增强扩展,其中还有一些行为是类似的:

ce1002066c5e7fc6bfa59b248dcf7c0b.png

事实证明,异步IO的基础是生成器函数(无论是使用 async 还是旧式的 @asyncio.coroutine 声明协程)。从技术上讲,相比于 yieldawait 更接近于 yield from。(但是有一点要记住,yield from 仅仅是用来替换 for i in x(): yield i 的语法糖。)

为什么生成器适合用来实现异步IO,就是它可以随意的停止和重启。例如,你可以在迭代一个生成器对象时暂停,然后在迭代器剩余的值上恢复迭代。当一个生成器函数运行到 yield 时,它会抛出这个值,然后将处于空闲状态,直到需要抛出新的值。

可以通过一个例子加深理解:

8ffcdb20157fad25e9d15eb9da32e0f3.png

关键字 await 与之行为类似,在协程自身被挂起并告知其他协程开始工作的时候会标记一个断点。这里的”挂起“是指暂时交出控制权限,但却不是完全退出或结束。记住,yield,以及拓展出的 yield from 和 await 都会在协程执行的时候标记一个断点。

这是函数与生成器之间的根本区别。函数是要么都执行,要么都不执行。函数一旦开始执行,除非是遇到 return,否则不会停止,然后将值返回并推给调用方(函数的调用者)。与之相对,生成器每当遇到 yield 都会暂停不在运行,它不仅可以将值返回给调用堆栈,还可以在使用 next() 恢复调用时保留其中的局部变量。

生成器的另一个少为人知的重要功能。你可以通过 .send() 向生成器中发送一个值,这就允许生成器(以及协程)调用(await)其他方法而不会阻塞。我不会深入探究这个功能实现的细节,因为它主要是为了在幕后实现协程,你不需要也不应该直接使用这种方式。

如果你有兴趣了解更多内容,可以看一看PEP 342,Python从PEP 342开始正式引入了协程。Brett Cannon 的Python中async-await工作原理也值得一读,还有PYMOTW对asyncio的评述。最后,还有 David Beazley 的协程与并发的探讨,深入探讨了关于协程运行的机制。

将上面的文章精简成几句话:协程通过一种非常规的机制运行,当协程调用 .send() 时,返回的结果是调用返回时抛出的异常对象的属性。这些还有一些更复杂的细节,但是对于实践却没有什么帮助,我们继续讲下面的。

为了方便总结,这里列出了一些协程作为生成器的关键点:

    1. 协程是根据生成器方法的高级特性并重新规划后的方法

    2. 旧式的基于生成器的协程使用 yield from 等待协程的结果。新式的Python语法中原生协

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值