Timothy was studying a function that traversed a nested directory structure, and his forehead was creased with confusion. "Margaret, I'm trying to understand this code. It has yield from in it, and I can't figure out what it's doing differently than just yield."
Margaret looked over his shoulder at the screen. "Ah, generator delegation. This is one of Python's more elegant features, but it's subtle. Let me show you why it exists and what the structure reveals."
Timothy's code was attempting to flatten a nested list structure:
def flatten(items):
result = []
for item in items:
if isinstance(item, list):
for sub_item in flatten(item):
result.append(sub_item)
else:
result.append(item)
return result
"This works," Timothy said, "but I saw someone use yield from to do the same thing in fewer lines. I tried to rewrite it but I'm not sure I understand the structure."
The Problem: Manual Iteration Over Generators
"Your version builds a list," Margaret observed. "That means you're storing everything in memory at once. Watch what happens when we use generators and yield from instead."
She rewrote the function:
def flatten(items):
for item in items:
if isinstance(item, list):
yield from flatten(item)
else:
yield item
"Wait," Timothy blinked. "That's... much shorter. But what's yield from actually doing?"
"Let me show you the structure."
Tree View:
flatten(items)
For item in items
If isinstance(item, list)
├── (yield from flatten(item))
└── Else
(yield item)
English View:
Function flatten(items):
For each item in items:
If isinstance(item, list):
Evaluate (yield from flatten(item)).
else:
Evaluate (yield item).
Timothy studied the structure carefully. "So yield from is... delegating to another generator?"
"Exactly," Margaret said. "When you write yield from flatten(item), you're telling Python: 'I'm delegating iteration to this other generator. Whatever it yields, yield that directly to my caller.'"
Understanding the Pattern
Margaret pulled out another example to make the concept clearer:
def read_files(filenames):
for filename in filenames:
with open(filename, 'r') as f:
yield from f
Tree View:
read_files(filenames)
For filename in filenames
With open(filename, 'r') as f
(yield from f)
English View:
Function read_files(filenames):
For each filename in filenames:
With open(filename, 'r') as f:
Evaluate (yield from f).
"See?" Margaret pointed at the structure. "The yield from f means 'delegate iteration to the file object. Each line it yields, I yield.'"
Timothy's eyes widened. "So instead of writing a loop that yields each line individually, yield from just... connects the two generators together?"
"That's a perfect way to think about it," Margaret confirmed. "It's generator plumbing. You're connecting one generator's output directly to another's input."
She showed him what the manual version would look like:
def read_files_manual(filenames):
for filename in filenames:
with open(filename, 'r') as f:
for line in f:
yield line
Tree View:
read_files_manual(filenames)
For filename in filenames
With open(filename, 'r') as f
For line in f
(yield line)
English View:
Function read_files_manual(filenames):
For each filename in filenames:
With open(filename, 'r') as f:
For each line in f:
Evaluate (yield line).
"Now look at the difference in structure," Margaret said. "The manual version has three levels of nesting. The yield from version has two. When you're delegating to another generator, yield from expresses that delegation directly."
Timothy compared the two structures side by side. "So yield from is saying 'I trust this other generator to handle iteration. Just pass through whatever it yields.'"
"Precisely. And it's not just about fewer lines of code - it's about expressing intent. When you see yield from, you know immediately that this generator is delegating responsibility to another generator."
"Does it work with any iterable?" Timothy asked.
"Yes. You can yield from any iterable - lists, tuples, generators, files, even other objects that implement the iterator protocol. Python handles the delegation."
Timothy nodded slowly, then looked back at his nested list flattener. "So in my flatten function, when I hit a nested list, I'm recursively delegating to another flatten generator, and it yields all its items directly to my caller?"
"Exactly. The structure shows it clearly - you're not manually iterating and re-yielding. You're delegating the entire iteration to the recursive call."
Margaret leaned back. "Generator delegation is powerful because it lets you compose generators like building blocks. Each generator does one thing, and yield from connects them together structurally."
Timothy saved his refactored code, already thinking about other places where manual iteration loops could become clean delegation. "First I learned about else clauses I didn't know existed. Now I'm learning about delegation I didn't know existed. Python keeps surprising me."
"The structure is always there," Margaret smiled. "You just needed to learn how to see it."
Explore Python structure yourself: Download the Python Structure Viewer - a free tool that shows code structure in tree and plain English views. Works offline, no installation required.
Aaron Rose is a software engineer and technology writer at tech-reader.blog and the author of Think Like a Genius.

Top comments (0)