Skip to main content
🌊
PEP 530 · Speedrun · Python 3.6 (2016)

PEP 530 in 90 seconds — async list/dict/set comprehensions

Sibling of async generators — pipelines in one expression.

PEP 525 gave us `async def` + `yield` (async generators). PEP 530 (Python 3.6, 2016) closed the loop by letting `async for` AND `await` appear inside **comprehensions**: ```python rows = [transform(r) async for r in db.cursor] awaited = [await fetch(url) for url in urls] ``` Before 530, the comprehension head couldn't be `async for`, so streaming a cursor into a list meant an explicit `async for` loop. Same for dict and set comprehensions.

What you can write 3.6+: - `[expr async for x in async_gen]` - `{key: expr async for x in async_gen}` - `{expr async for x in async_gen}` (set) - `[await fn(x) for x in iterable]` (await in comprehension body — also new in 3.6 via PEP 530) Real-world: every `httpx.AsyncClient` example you've seen, every async DB driver tutorial, every LLM batch-call snippet uses this. It looks ordinary precisely because it was added so early.

Before — Python ≤ 3.5, manual async for loop
import asyncio

async def stream(n):
    for i in range(n):
        yield i

async def main():
    # Pre-3.6: explicit accumulator loop
    squares = []
    async for x in stream(5):
        squares.append(x ** 2)
    print(squares)
After — Python 3.6+, async comprehensions
import asyncio

async def stream(n):
    for i in range(n):
        yield i

async def main():
    # 3.6+ — one expression
    squares = [x ** 2 async for x in stream(5)]
    cubes = {x: x ** 3 async for x in stream(4)}
    odds = {x async for x in stream(7) if x % 2 == 1}

    # And await inside the body works too
    awaited = [await asyncio.sleep(0, result=i) for i in range(3)]

asyncio.run(main())

🎯 Predict the output

What does this print? `async for` collects from the generator, then the regular expression `x ** N` runs per item.

import asyncio

async def numbers(n):
    for i in range(n):
        yield i

async def main():
    squares = [x ** 2 async for x in numbers(5)]
    print(squares)
    cubes = {x: x ** 3 async for x in numbers(4)}
    print(cubes)
    items = [await asyncio.sleep(0, result=i) for i in range(3)]
    print(items)

asyncio.run(main())
Async pipelines deep-dive → Foundations track

Or speedrun another PEP

PEP 572Assignment as an expression — 3 lines become 1.
PEP 572 in 90 seconds — the walrus that ate your for-loop
PEP 484Python stays dynamic — but you can opt into typing.
PEP 484 in 90 seconds — type hints, without TypeScript-envy
PEP 622Switch statements are a 5% feature. Pattern matching is the 95%.
PEP 622 in 90 seconds — match / case isn't just a switch