这个问题是由我的另一个问题推动的:如何在cdef中等待?
网上有大量的文章和博客文章asyncio,但它们都非常肤浅.我找不到任何有关如何asyncio实际实现的信息,以及I/O异步的原因.我试图阅读源代码,但它是成千上万行不是最高级别的C代码,其中很多处理辅助对象,但最重要的是,很难在Python语法和它将翻译的C代码之间建立连接成.
Asycnio自己的文档甚至没那么有用.没有关于它是如何工作的信息,只有关于如何使用它的一些指导,这些指导有时也会误导/写得很差.
我熟悉Go的coroutines实现,并且希望Python做同样的事情.如果是这种情况,我在上面链接的帖子中出现的代码就可以了.既然没有,我现在正试图找出原因.到目前为止我最好的猜测如下,请纠正我错在哪里:
async def foo(): ...实际上被解释为类继承的方法coroutine.async def实际上是通过await语句拆分成多个方法,其中调用这些方法的对象能够跟踪到目前为止通过执行所做的进度.await语句) ).换句话说,这是我尝试将某些asyncio语法"贬低"为更容易理解的东西:
async def coro(name):
print('before', name)
await asyncio.sleep()
print('after', name)
asyncio.gather(coro('first'), coro('second'))
# translated from async def coro(name)
class Coro(coroutine):
def before(self, name):
print('before', name)
def after(self, name):
print('after', name)
def __init__(self, name):
self.name = name
self.parts = self.before, self.after
self.pos = 0
def __call__():
self.parts[self.pos](self.name)
self.pos += 1
def …Run Code Online (Sandbox Code Playgroud) PEP 0492增加了新的__await__魔法.实现此方法的对象变为类似未来的对象,可以等待使用await.很明显:
import asyncio
class Waiting:
def __await__(self):
yield from asyncio.sleep(2)
print('ok')
async def main():
await Waiting()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Run Code Online (Sandbox Code Playgroud)
好的,但如果我想调用一些已async def定义的函数而不是asyncio.sleep?我不能使用await因为__await__不是async函数,我无法使用yield from因为本机协同程序需要await表达式:
async def new_sleep():
await asyncio.sleep(2)
class Waiting:
def __await__(self):
yield from new_sleep() # this is TypeError
await new_sleep() # this is SyntaxError
print('ok')
Run Code Online (Sandbox Code Playgroud)
我该如何解决?
基于生成器的协同程序具有send()允许调用者和被调用者之间的双向通信并且从调用者恢复生成的生成器协同程序的方法.这是将生成器转换为协同程序的功能.
虽然新的本机async/await协同程序为异步I/O提供了出色的支持,但我看不出如何send()使用它们.明确禁止使用yieldin async函数,因此本机协同程序只能使用return语句返回一次.虽然await表达式为协程带来了新的值,但这些值来自被叫者,而不是来电者,等待的呼叫每次都从头开始评估,而不是从它停止的地方开始.
有没有办法从它停止的地方恢复返回的协同程序,并可能发送一个新值?如何使用本机协同程序模拟David Beazley 关于协同程序和并发的好奇课程中的技巧?
我想到的一般代码模式就像
def myCoroutine():
...
while True:
...
ping = yield(pong)
...
Run Code Online (Sandbox Code Playgroud)
并在呼叫者
while True:
...
buzz = myCoroutineGen.send(bizz)
...
Run Code Online (Sandbox Code Playgroud)
我接受了Kevin的回答,但我注意到PEP 说
协同程序在内部基于生成器,因此它们共享实现.与生成器对象类似,协同程序具有throw(),send()和close()方法.
...
用于协同程序的throw(),send()方法用于推送值并将错误引发到类似Future的对象中.
显然,原生协同程序确实有一个send()?如果没有yield表达式来接收协程内的值,它如何工作?
我想知道当我们await在异步 Python 代码中使用协程时究竟会发生什么,例如:
await send_message(string)
Run Code Online (Sandbox Code Playgroud)
(1)send_message加入事件循环,调用协程放弃对事件循环的控制,或
(2) 我们直接跳进去 send_message
我读到的大多数解释都指向 (1),因为它们将调用协程描述为exiting。但我自己的实验表明(2)是这种情况:我试图在调用者之后但在被调用者之前运行一个协程,但无法实现这一点。