yield from and generator delegation

RoadmapsPython

Overview

The `yield from` expression delegates part of a generator's operations to another iterable (called the subgenerator). It acts as a transparent two-way pipe. Instead of iterating with a `for` loop and yielding items one by one, `yield from` automatically yields all values from the sub-iterable. Think of it like connecting a hose to an existing tap instead of carrying water by buckets. A sharp edge for developers is ignoring the return value of a subgenerator. If a subgenerator executes a `return` statement, that value becomes the result of the `yield from` expression (covered fully in generator-send): def sub(): yield 1 return "done" def main(): res = yield from sub() print(res) # Prints: done

It removes boilerplate when one generator needs to `yield` all elements from another. This makes recursive generators clean and enables advanced coroutine patterns.

Where used: Recursive data structure traversal, Coroutines (`asyncio` foundation), Chunked data processing

Why learn this

Code walkthrough

def sub_gen():
    yield 1
    yield 2

def main_gen():
    yield 'start'
    yield from sub_gen()
    yield 'end'

for item in main_gen():
    print(item)

Focus: yield from sub_gen()

Aha moment

def get_letters():
    yield from 'AB'

def get_numbers():
    yield from [1, 2]

def combined():
    yield from get_letters()
    yield from get_numbers()

print(list(combined()))

Prediction: What does this print?

Common guess: ['AB', [1, 2]]

`yield from` delegates to the string iterable (`'A'`, `'B'`) and then the list iterable (`1`, `2`), effectively chaining them together into `['A', 'B', 1, 2]`.

Common mistakes

Glossary

subgenerator
A smaller, helper generator that is called upon by a main generator to produce a series of values. Example: `def sub_gen(): yield 1`.
coroutine patterns
Common ways of organizing code using functions that can pause and resume, allowing multiple tasks to run cooperatively. Example: `asyncio` loops.

Recall questions

Understanding checks

What is the output of this code?

[1, 'a', 'b', 2]

The `main` generator yields `1`, then pauses its own yielding to fully exhaust `sub()` (yielding `'a'` and `'b'`), and then resumes to yield `2`.

Why use `yield from` instead of manually looping over a sub-iterable and yielding each item?

It eliminates boilerplate code and establishes a transparent two-way channel between the caller and the subgenerator.

For simple generators, it saves lines of code. For advanced use cases, it allows values and exceptions to be sent directly to the subgenerator.

What is the output of this code?

['a', 'b']

The `yield from` expression yields `'a'`. When `sub()` returns `'b'`, the `yield from` expression evaluates to `'b'`, which is assigned to `res`. The `main` generator then yields `res`.

Practice tasks

Flatten a Nested List

Refactor the `flatten` generator to use `yield from` instead of the inner `for` loop to yield items from each sublist.

Challenge

Recursive Generator

Write a generator `traverse_tree` that recursively yields all node values in a binary tree from left to right. The tree is represented as a dictionary: `{'val': 1, 'left': {...}, 'right': {...}}`. Assume missing children are `None`. Use `yield from` for the recursive calls.

Continue learning

Previous: Iterator Protocol and StopIteration

Previous: The Iterable Protocol

Next: Generator send() for Two-Way Communication

Return to Python Roadmap