Конечно, можно запустить функцию async
без явного использования asyncio
. В конце концов, asyncio
написан на Python, поэтому все это можно сделать (хотя иногда вам могут понадобиться другие модули, такие как selectors
или threading
, если вы намерены одновременно ждать внешних событий или выполнять несколько других кодов).
В этом случае, поскольку ваша функция не имеет await
точек внутри, для этого требуется всего лишь одно нажатие. Вы нажимаете coroutine на send
ing 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
s начинают получать утомительно. Было бы неплохо получить их полуавтоматически.
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 РЕПЛ печатали подсказки в окно консоли, и рассчитывает на вас, чтобы обеспечить события, набрав t.send(whatever)
в него :)
asyncio
является лишь очень обобщенным вариантом выше:. наводящие заменяется Future
с, несколько сопрограмм находятся в очереди, так что каждый из них в конце концов, получает свою очередь, и событие гораздо более богатые и включают в себя сетевую/сотовую связь, чтение/запись файловой системы, обработку сигналов, обработку потока/процесса и т. д. Но основная идея все та же: вы хватаете некоторые сопрограммы, жонглируете их в воздухе, направляя фьючерсы от одного к другому, пока все не поднимут StopIteration
. Когда все сопрограммы не имеют ничего общего, вы отправляетесь во внешний мир и захватываете некоторые дополнительные события, чтобы они могли жевать и продолжать.
Надеюсь, теперь все намного понятнее. :-)
Что касается истинных сопрограмм на Python 3.5, я сомневаюсь в этом, потому что что-то должно вращать колесо, так сказать. Хотя вы могли бы попробовать внедрить сопрограмму «старомодным» способом, создав генератор python и подав ее с помощью команд 'send()' – shongololo
. Почему вы не хотите импортировать 'asyncio'? – dirn