Рассмотрим следующую программу (запущенную на CPython 3.4.0b1):
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
, и возвращается обычныйfloat
. Которая затем взрывается вrun_test()
вyield from
-
Почему мне нужно оценить
future.result()
вrun_test
, чтобы получить значение огненного исключения? docs 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)
Хорошо, я нашел "проблему". yield from asyncio.sleep
в slow_sqrt
сделает это сопрограммой автоматически. Ожидание должно выполняться по-другому:
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 варианта здесь.