当然可以在不显式使用asyncio 的情况下启动async 函数。毕竟,asyncio 是用 Python 编写的,所以它所做的一切,你也可以做(尽管有时你可能需要其他模块,如 selectors 或 threading,如果你打算同时等待外部事件,或者并行执行一些其他代码)。
在这种情况下,由于您的函数内部没有 await 点,因此只需按一下即可。您通过 sending None 将协程推入其中。
>>> foo().send(None)
Hello!
Hello!
...
当然,如果你的函数(协程)内部有 yield 表达式,它会在每个 yield 点暂停执行,并且你需要将其他值推入其中(通过 coro.send(value) 或 next(gen)) - 但如果你知道生成器是如何工作的,你就已经知道了。
import types
@types.coroutine
def bar():
to_print = yield 'What should I print?'
print('Result is', to_print)
to_return = yield 'And what should I return?'
return to_return
>>> b = bar()
>>> next(b)
'What should I print?'
>>> b.send('Whatever you want')
Result is Whatever you want
'And what should I return?'
>>> b.send(85)
Traceback...
StopIteration: 85
现在,如果您的函数内部有 await 表达式,它会在评估每个表达式时暂停。
async def baz():
first_bar, second_bar = bar(), bar()
print('Sum of two bars is', await first_bar + await second_bar)
return 'nothing important'
>>> t = baz()
>>> t.send(None)
'What should I print?'
>>> t.send('something')
Result is something
'And what should I return?'
>>> t.send(35)
'What should I print?'
>>> t.send('something else')
Result is something else
'And what should I return?'
>>> t.send(21)
Sum of two bars is 56
Traceback...
StopIteration: nothing important
现在,所有这些.sends 都开始变得乏味了。最好能半自动生成它们。
import random, string
def run_until_complete(t):
prompt = t.send(None)
try:
while True:
if prompt == 'What should I print?':
prompt = t.send(random.choice(string.ascii_uppercase))
elif prompt == 'And what should I return?':
prompt = t.send(random.randint(10, 50))
else:
raise ValueError(prompt)
except StopIteration as exc:
print(t.__name__, 'returned', exc.value)
t.close()
>>> run_until_complete(baz())
Result is B
Result is M
Sum of two bars is 56
baz returned nothing important
恭喜,您刚刚编写了您的第一个事件循环! (没想到它会发生,是吗?;)当然,它非常原始:它只知道如何处理两种类型的提示,它不能让t 生成与它同时运行的额外协程,它通过random 生成器伪造事件。
(事实上,如果你想深入浅出:我们在上面手动执行的操作,也可以称为事件循环:Python REPL 将提示打印到控制台窗口,它依赖于您可以通过在其中输入t.send(whatever) 来提供事件。:)
asyncio 只是上面的一个非常通用的变体:提示被Futures 取代,多个协程保持在队列中,因此最终轮到它们每个,事件更丰富,包括网络/套接字通信、文件系统读/写、信号处理、线程/进程侧执行等。但是基本的想法还是一样的:你抓住一些协程,在空中将它们从一个路由到另一个,直到它们都加注StopIteration。当所有协程都无事可做时,你去外部世界抓取一些额外的事件让它们咀嚼,然后继续。
我希望现在一切都清楚多了。 :-)