Чтобы сохранить стек, вы должны разрешить каждому сопрограмме фактически выйти после того, как он планирует следующий рекурсивный вызов, а это значит, что вам нужно избегать использования yield from
. Вместо этого вы используете asyncio.async
(или asyncio.ensure_future
, если используете Python 3.4.4+), чтобы запланировать следующую сопрограмму с циклом событий и использовать Future.add_done_callback
, чтобы запланировать обратный вызов для запуска после возврата рекурсивного вызова. Затем каждый coroutine возвращает объект asyncio.Future
, который имеет свой результат в обратном вызове, который запускается при завершении рекурсивного вызова, который он запланировал.
Это, наверное, проще всего понять, если вы на самом деле видите код:
import asyncio
@asyncio.coroutine
def a(n):
fut = asyncio.Future() # We're going to return this right away to our caller
def set_result(out): # This gets called when the next recursive call completes
fut.set_result(out.result()) # Pull the result from the inner call and return it up the stack.
print("A: {}".format(n))
if n > 1000:
return n
else:
in_fut = asyncio.async(b(n+1)) # This returns an asyncio.Task
in_fut.add_done_callback(set_result) # schedule set_result when the Task is done.
return fut
@asyncio.coroutine
def b(n):
fut = asyncio.Future()
def set_result(out):
fut.set_result(out.result())
print("B: {}".format(n))
in_fut = asyncio.async(a(n+1))
in_fut.add_done_callback(set_result)
return fut
loop = asyncio.get_event_loop()
print("Out is {}".format(loop.run_until_complete(a(0))))
Output:
A: 0
B: 1
A: 2
B: 3
A: 4
B: 5
...
A: 994
B: 995
A: 996
B: 997
A: 998
B: 999
A: 1000
B: 1001
A: 1002
Out is 1002
Теперь, ваш пример код на самом деле не возвращать n
весь путь обратно в стек, так что вы могли бы сделать что-то функционально эквивалентным это немного проще:
import asyncio
@asyncio.coroutine
def a(n):
print("A: {}".format(n))
if n > 1000: loop.stop(); return n
else: asyncio.async(b(n+1))
@asyncio.coroutine
def b(n):
print("B: {}".format(n))
asyncio.async(a(n+1))
loop = asyncio.get_event_loop()
asyncio.async(a(0))
loop.run_forever()
Но я подозреваю, что вы на самом деле имел в виду, чтобы вернуть n
весь путь обратно.
каждый раз, когда вы получаете от «вы подключаетесь к следующему вызову. Вы пытались использовать очередь вместо этого? Таким образом, вы можете передать информацию и удалить ее в следующую сопрограмму, не связывая их друг с другом. – shongololo