Python的ASYNCIO,期货收益率从
考虑下面的程序(在CPython的运行3.4.0b1):Python的ASYNCIO,期货收益率从
import math
import asyncio
from asyncio import coroutine
@coroutine
def fast_sqrt(x):
future = asyncio.Future()
if x >= 0:
future.set_result(math.sqrt(x))
else:
future.set_exception(Exception("negative number"))
return future
def slow_sqrt(x):
yield from asyncio.sleep(1)
future = asyncio.Future()
if x >= 0:
future.set_result(math.sqrt(x))
else:
future.set_exception(Exception("negative number"))
return future
@coroutine
def run_test():
for x in [2, -2]:
for f in [fast_sqrt, slow_sqrt]:
try:
future = yield from f(x)
print("\n{} {}".format(future, type(future)))
res = future.result()
print("{} result: {}".format(f, res))
except Exception as e:
print("{} exception: {}".format(f, e))
loop = asyncio.get_event_loop()
loop.run_until_complete(run_test())
我有2个(相关的)问题:
即使在装饰上
fast_sqrt
,Python似乎会优化掉fast_sqrt
中创建的Future,并返回一个普通的float
。然后在run_test()
在yield from
炸毁为什么我需要评估在
run_test
future.result()
检索火的值例外呢? docs说yield from <future>
“暂停协程,直到未来完成,然后返回未来的结果,或引发异常”。为什么我需要手动调整未来的结果?
这里是我所得到的:
[email protected] ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master)
$ python3 -V
Python 3.4.0b1
[email protected] ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master)
$ python3 test3.py
1.4142135623730951 <class 'float'>
<function fast_sqrt at 0x00B889C0> exception: 'float' object has no attribute 'result'
Future<result=1.4142135623730951> <class 'asyncio.futures.Future'>
<function slow_sqrt at 0x02AC8810> result: 1.4142135623730951
<function fast_sqrt at 0x00B889C0> exception: negative number
Future<exception=Exception('negative number',)> <class 'asyncio.futures.Future'>
<function slow_sqrt at 0x02AC8810> exception: negative number
[email protected] ~/scm/tavendo/infrequent/scratchbox/python/asyncio (master)
好吧,我发现了 “问题”。在slow_sqrt
中的yield from asyncio.sleep
将自动使其成为协程。等待需要以不同的方式完成:
def slow_sqrt(x):
loop = asyncio.get_event_loop()
future = asyncio.Future()
def doit():
if x >= 0:
future.set_result(math.sqrt(x))
else:
future.set_exception(Exception("negative number"))
loop.call_later(1, doit)
return future
所有4个变体都是here。
关于#1:Python没有这样的事情。请注意,您编写的fast_sqrt
函数(即在任何装饰器之前)不是生成器函数,协程函数,任务或任何您想调用的函数。这是一个同步运行的普通函数,返回return
语句后写入的内容。根据@coroutine
的存在,会发生非常不同的事情。只是运气不好,都会导致相同的错误。
没有装饰,
fast_sqrt(x)
运行像普通函数是和返回float的未来(不管上下文的)。future = yield from ...
将消耗该未来,并将future
保留为浮点型(其没有result
方法)。随着装饰,呼叫
f(x)
经过由@coroutine
创建一个包装函数。这个包装函数调用fast_sqrt
,并使用yield from <future>
构造为您解包生成的未来。因此,这个包装函数本身就是一个协程。因此,future = yield from ...
等即协同程序和叶future
再次浮动。
关于#2,yield from <future>
确实工作(如上所述,您使用的是未经修饰的fast_sqrt
在使用它),你也可以这样写:
future = yield from coro_returning_a_future(x)
res = yield from future
(模,它对于fast_sqrt
不起作用,因为写入时不会产生额外的异步性,因为将来从coro_returning_a_future
返回时已经完成。)
您的核心问题se ems是你混淆了协程和期货。您的sqrt实现都尝试成为导致期货的异步任务。 从我有限的经验来看,这不是通常写asyncio代码的方式。它允许您将未来的构建和将来代表的计算分解为两个独立的异步任务。但是你不这样做(你返回一个已经完成的未来)。在大多数情况下,这不是一个有用的概念:如果你必须异步做一些计算,你可以将它作为协同程序(可以挂起)或写入另一个线程并与之通信使用yield from <future>
。不是都。
为了平方根计算异步,只写一个普通协程做了计算和return
结果(coroutine
装饰会变成fast_sqrt
成异步运行任务,可以在被等待)。
@coroutine
def fast_sqrt(x):
if x >= 0:
return math.sqrt(x)
else:
raise Exception("negative number")
@coroutine # for documentation, not strictly necessary
def slow_sqrt(x):
yield from asyncio.sleep(1)
if x >= 0:
return math.sqrt(x)
else:
raise Exception("negative number")
...
res = yield from f(x)
assert isinstance(res, float)
我知道如何处理协程和没有期货(https://github.com/oberstet/scratchbox/blob/master /python/asyncio/test2.py),但是这个_requires_函数要被修饰。我正在寻找期货和无装饰者(非侵入性的rgd API)的解决方案。 – oberstet
@oberstet您可以编写一个返回未来的普通非协程函数。您可以编写一个普通函数来返回普通值并同步使用它们。你可以在没有装饰器的情况下编写协程(虽然你显然仍然需要遵循关于'yield from'等的asyncio约定)。我不知道什么是“rgd API”,但如果你想更详细地解决你的问题,你可以打开另一个问题。 – delnan
我的“问题”是'slow_srqt'内的'asyncio.sleep'产生的结果使它自动成为协程。我正在寻找这4个变种:https://github.com/oberstet/scratchbox/blob/master/python/asyncio/test3.py – oberstet
这是在Python3.3还是只是在测试版中的错误? – Mark
我没有3.3安装..会检查。 – oberstet
关于第一个问题,*哪里*爆炸了?在'未来=产量从f(x)'或'res = future.result()'? – delnan