Ответ 1
Относительно # 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>
работает (как объяснялось выше, вы используете его при использовании unecorated fast_sqrt
), и вы также можете написать:
future = yield from coro_returning_a_future(x)
res = yield from future
(По модулю, что он не работает для fast_sqrt
, как написано, и не получает дополнительной асинхронности, потому что будущее уже выполняется к моменту его возврата из coro_returning_a_future
.)
Ваша основная проблема заключается в том, что вы путаете сопрограммы и фьючерсы. Обе реализации sqrt пытаются выполнить асинхронные задачи, приводящие к фьючерсам.
Из моего ограниченного опыта это не то, как обычно пишется асинхронный код. Это позволяет вам тянуть как строительство будущего, так и вычисления, которые будущее означает для двух независимых задач async. Но вы этого не делаете (вы возвращаете уже готовое будущее). И большую часть времени это не очень полезная концепция: если вам нужно выполнить какое-то вычисление асинхронно, вы либо записываете его как сопрограмму (которая может быть приостановлена), либо вы вставляете ее в другой поток и обмениваетесь ею с помощью 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)