是否可以启动这样的功能
async def foo():
while True:
print("Hello!")
没有导入asyncio
包(并获取事件循环)?
我正在寻找一个类似于Go的goroutines的原则,其中一个人可以只用go
语句启动一个协程。
编辑:我之所以不导入asyncio包的原因仅仅是因为我认为应该可以在没有事件循环(显式)的情况下启动协同程序。我不明白为什么 async def 和类似的语句是核心语言的一部分(甚至是语法的一部分),启动创建的协同程序的方法只能通过包。
答案 0 :(得分:26)
当然可以在不明确使用async
的情况下启动asyncio
功能。毕竟,asyncio
是用Python编写的,所以它可以做到(你有时可能需要其他模块,如selectors
或threading
,如果你打算同时等待外部事件,或者可以执行其他一些代码。)
在这种情况下,由于你的函数里面没有await
点,所以只需要一次推动即可。您可以通过send
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
现在,所有这些.send
开始变得乏味。让它们半自动生成会很好。
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
只是上述的一个非常概括的变体:提示被Future
替换,多个协同程序保存在队列中,因此每个协程最终都会轮到它,事件更丰富,包括网络/套接字通信,文件系统读/写,信号处理,线程/进程侧执行等。但基本的想法仍然是相同的:你抓住一些协同程序,将它们从空中转移到另一个地方,直到它们全部抬起StopIteration
。当所有协同程序无关时,你会去外部世界并抓住一些额外的事件让他们咀嚼,然后继续。
我希望现在更清楚了。 : - )
答案 1 :(得分:1)
不,那是不可能的。你需要一个事件循环。看看如果你只是致电foo()
:
>>> f = foo()
>>> print(f)
<coroutine object foo at 0x7f6e13edac50>
所以你得到一个协程对象,现在没有任何东西被执行!只有将它传递给事件循环才能执行。您可以使用asyncio
或其他事件循环,例如Curio。
答案 2 :(得分:1)
Python协同程序是生成器的一种语法糖,它们的行为有一些额外的限制(因此它们的目的明显不同而且不混合)。你做不到:
next(foo())
TypeError: 'coroutine' object is not an iterator
因为它已被明确禁用。但是你可以这样做:
foo().send(None)
Hello
Hello
Hello
...
对于生成器,这相当于next()
。
答案 3 :(得分:0)
协同程序应该能够
运行
对调用者进行控制(可选择生成一些中间体 结果)
能够从来电者处获取一些信息并恢复
所以,这是一个异步函数(也称为本地协同程序)的小型演示,它不使用asyncio
或任何其他提供事件循环的模块/框架来完成它。至少需要python 3.5。请参阅代码中的注释。
#!/usr/bin/env python
import types
# two simple async functions
async def outer_af(x):
print("- start outer_af({})".format(x))
val = await inner_af(x) # Normal way to call native coroutine.
# Without `await` keyword it wouldn't
# actually start
print("- inner_af result: {}".format(val))
return "outer_af_result"
async def inner_af(x):
print("-- start inner_af({})".format(x))
val = await receiver() # 'await' can be used not only with native
# coroutines, but also with `generator-based`
# coroutines!
print("-- received val {}".format(val))
return "inner_af_result"
# To yiled execution control to caller it's necessary to use
# 'generator-based' coroutine: the one created with types.coroutine
# decorator
@types.coroutine
def receiver():
print("--- start receiver")
# suspend execution / yield control / communicate with caller
r = yield "value request"
print("--- receiver received {}".format(r))
return r
def main():
# We want to call 'outer_af' async function (aka native coroutine)
# 'await' keyword can't be used here!
# It can only be used inside another async function.
print("*** test started")
c = outer_af(42) # just prepare coroutine object. It's not running yet.
print("*** c is {}".format(c))
# To start coroutine execution call 'send' method.
w = c.send(None) # The first call must have argument None
# Execution of coroutine is now suspended. Execution point is on
# the 'yield' statement inside the 'receiver' coroutine.
# It is waiting for another 'send' method to continue.
# The yielded value can give us a hint about what exectly coroutine
# expects to receive from us.
print("*** w = {}".format(w))
# After next 'send' the coroutines execution would finish.
# Even though the native coroutine object is not iterable it will
# throw StopIteration exception on exit!
try:
w = c.send(25)
# w here would not get any value. This is unreachable.
except StopIteration as e:
print("*** outer_af finished. It returned: {}".format(e.value))
if __name__ == '__main__':
main()
输出如下:
*** test started
*** c is <coroutine object outer_af at 0x7f4879188620>
- start outer_af(42)
-- start inner_af(42)
--- start receiver
*** w = value request
--- receiver received 25
-- received val 25
- inner_af result: inner_af_result
*** outer_af finished. It returned: outer_af_result
补充评论。
看起来不可能从本地协程内部产生控制权。 yield
函数内不允许async
!因此,有必要import types
并使用coroutine
装饰器。它做了一些黑魔法!坦率地说,我不明白为什么yield
被禁止,因此需要混合使用原生和基于生成器的协程。