PEP 380 in 90 seconds — yield from and the bones of async/await
Delegate a whole generator in one line — and skip the boilerplate forever.
Before `yield from`, delegating to a sub-generator was 4 lines of forwarding boilerplate: ```python def wrap(gen): for x in gen: yield x # one level of nesting — already tedious ``` For two levels (a generator yielding from another generator yielding from a third) it got worse — you couldn't easily forward `.send()`, `.throw()`, or the sub-generator's return value. PEP 380 (Python 3.3, 2012) collapsed it into one keyword.
`yield from sub_gen` does **all** of: - Yields every value `sub_gen` produces - Forwards `.send()` / `.throw()` from the outer caller to `sub_gen` - Captures `sub_gen`'s return value (Python 3.3+ generators can `return X` to end with a value) It's the layer `asyncio` was built on top of. `await x` in an `async def` is effectively `yield from x` underneath — just with the asyncio event loop driving the iteration.
def numbers():
yield 1
yield 2
yield 3
def wrap_old():
for x in numbers(): # boilerplate
yield x
yield 4
print(list(wrap_old())) # [1, 2, 3, 4]def numbers():
yield 1
yield 2
yield 3
def wrap_new():
yield from numbers() # delegates everything
yield 4
print(list(wrap_new())) # [1, 2, 3, 4]
# Also: forwards .send() / .throw() / captures return value:
def producer():
val = yield "hello"
return f"got: {val}"
def wrapper():
result = yield from producer()
yield result🎯 Predict the output
What does this print? `yield from` inlines every value of the sub-generator, in order.
def squares():
for i in range(3):
yield i ** 2
def combined():
yield "start"
yield from squares()
yield "end"
print(list(combined()))