Конечно, можно запустить функцию 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
. Когда всем сопрограммам нечего делать, вы отправляетесь во внешний мир и захватываете некоторые дополнительные события, которые они могут пережить, и продолжаете.
Надеюсь, теперь все стало намного яснее. :-)
person
Veky
schedule
28.07.2016
send()
. - person songololo   schedule 24.02.2016asyncio
? - person dirn   schedule 24.02.2016