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.
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)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())