I'm having trouble understanding the use of asynchronous comprehensions introduced in Python 3.6. As a disclaimer, I don't have a lot of experience dealing with asynchronous code in general in Python.
The example given in the what's new for Python 3.6 document is:
result = [i async for i in aiter() if i % 2]
In the PEP, this is expanded to:
result = [] async for i in aiter(): if i % 2: result.append(i)
I think I understand that the aiter()
function gets called asynchronously, so that each iteration of aiter
can proceed without the previous one necessarily returning yet (or is this understanding wrong?).
What I'm not sure about is how that then translates to the list comprehension here. Do results get placed into the list in the order that they are returned? Or are there effective 'placeholders' in the final list so that each result is placed in the list in the right order? Or am I thinking about this the wrong way?
Additionally, is someone able to provide a real-world example that would illustrate both an applicable use case and the basic mechanics of async
in comprehensions like this?
Asynchronous programming is a type of parallel programming in which a unit of work is allowed to run separately from the primary application thread. When the work is complete, it notifies the main thread about completion or failure of the worker thread.
To run an async function (coroutine) you have to call it using an Event Loop. Event Loops: You can think of Event Loop as functions to run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses. Example 1: Event Loop example to run async Function to run a single async function: Python3.
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.
asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc. asyncio is often a perfect fit for IO-bound and high-level structured network code.
You are basically asking how an async for
loop works over a regular loop. That you can now use such a loop in a list comprehension doesn't make any difference here; that's just an optimisation that avoids repeated list.append()
calls, exactly like a normal list comprehension does.
An async for
loop then, simply awaits each next step of the iteration protocol, where a regular for
loop would block.
To illustrate, imagine a normal for
loop:
for foo in bar: ...
For this loop, Python essentially does this:
bar_iter = iter(bar) while True: try: foo = next(bar_iter) except StopIteration: break ...
The next(bar_iter)
call is not asynchronous; it blocks.
Now replace for
with async for
, and what Python does changes to:
bar_iter = aiter(bar) # aiter doesn't exist, but see below while True: try: foo = await anext(bar_iter) # anext doesn't exist, but see below except StopIteration: break ...
In the above example aiter()
and anext()
are fictional functions; these are functionally exact equivalents of their iter()
and next()
brethren but instead of __iter__
and __next__
these use __aiter__
and __anext__
. That is to say, asynchronous hooks exist for the same functionality but are distinguished from their non-async variants by the prefix a
.
The await
keyword there is the crucial difference, so for each iteration an async for
loop yields control so other coroutines can run instead.
Again, to re-iterate, all this already was added in Python 3.5 (see PEP 492), all that is new in Python 3.6 is that you can use such a loop in a list comprehension too. And in generator expressions and set and dict comprehensions, for that matter.
Last but not least, the same set of changes also made it possible to use await <expression>
in the expression section of a comprehension, so:
[await func(i) for i in someiterable]
is now possible.
I think I understand that the
aiter()
function gets called asynchronously, so that each iteration ofaiter
can proceed without the previous one necessarily returning yet (or is this understanding wrong?).
That understanding is wrong. Iterations of an async for
loop cannot be performed in parallel. async for
is just as sequential as a regular for
loop.
The asynchronous part of async for
is that it lets the iterator await
on behalf of the coroutine iterating over it. It's only for use within asynchronous coroutines, and only for use on special asynchronous iterables. Other than that, it's mostly just like a regular for
loop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With