Is is possible to start a function like this
async def foo():
while True:
print("Hello!")
without importing the asyncio
package (and getting the event loop)?
I am looking for a principle similar to Go's goroutines, where one can launch a coroutine with only go
statement.
Edit: The reason why I'm not importing the asyncio package is simply because I think it should be possible to launch coroutine without event loop (explicit). I don't understand why async def and similar statements are part of core language (even part of syntax) and the way to launch created coroutines is available only through package.
Coroutines should be able to
run
yield control to caller (optionally producing some intermediate results)
be able to get some information from caller and resume
So, here is a small demo of async functions (aka native coroutines) which do it without using
asyncio
or any other modules/frameworks which provide event loop. At least python 3.5 is required. See comments inside the code.Output looks like:
Additional comment. Looks like it's not possible to yield control from inside native coroutine.
yield
is not permitted insideasync
functions! So it is necessary toimport types
and usecoroutine
decorator. It does some black magic! Frankly speaking I do not understand whyyield
is prohibited so that a mixture of native and generator-based coroutines is required.No, that is not possible. You need an event loop. Take a look at what happens if you just call
foo()
:So you get a coroutine object, nothing get's executed right now! Only by passing it to an event loop does it get executed. You can use
asyncio
or another event loop like Curio.Python coroutines are a syntactic sugar for generators, with some added restrictions in their behavior (so that their purpose is explicitly different and doesn't mix). You can't do:
because it's disabled explicitly. However you can do:
Which is equivalent to
next()
for a generator.Of course it is possible to start an
async
function without explicitly usingasyncio
. After all,asyncio
is written in Python, so all it does, you can do too (though sometimes you might need other modules likeselectors
orthreading
if you intend to concurrently wait for external events, or paralelly execute some other code).In this case, since your function has no
await
points inside, it just needs a single push to get going. You push a coroutine bysend
ingNone
into it.Of course, if your function (coroutine) had
yield
expressions inside, it would suspend execution at eachyield
point, and you would need to push additional values into it (bycoro.send(value)
ornext(gen)
) - but you already know that if you know how generators work.Now, if your function had
await
expressions inside, it would suspend at evaluating each of them.Now, all these
.send
s are starting to get tedious. It would be nice to have them semiautomatically generated.Congratulations, you just wrote your first event loop! (Didn't expect it to happen, did you?;) Of course, it is horribly primitive: it only knows how to handle two types of prompts, it doesn't enable
t
to spawn additional coroutines that run concurrently with it, and it fakes events by arandom
generator.(In fact, if you want to get philosophical: what we did above that manually, could also be called an event loop: Python REPL was printing prompts to a console window, and it was relying on you to provide events by typing
t.send(whatever)
into it.:)asyncio
is just an immensely generalized variant of the above: prompts are replaced byFuture
s, multiple coroutines are kept in queues so each of them eventually gets its turn, and events are much richer and include network/socket communication, filesystem reads/writes, signal handling, thread/process side-execution, and so on. But the basic idea is still the same: you grab some coroutines, juggle them in the air routing the Futures from one to another, until they all raiseStopIteration
. When all coroutines have nothing to do, you go to external world and grab some additional events for them to chew on, and continue.I hope it's all much clearer now. :-)