Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
172: yield from and Delegating Generators
When you first encounter yield from, it's easy to assume it's just some syntactic sugar—a lazier way of writing a for loop to yield items from another iterable. I see this all the time. Learners think, "Why bother learning a new keyword when I can just loop through a list and yield each element?"
Thinking yield from is just a for-loop shortcut
Let's look at why that mindset is limiting. If you use a for loop to delegate to another generator, you're essentially acting as a middleman who manually passes packages from one person to another. It works for simple data retrieval, but it breaks the moment you need to send information back into the generator.
def sub_generator():
# We want to receive a configuration value from the caller
config = yield "Ready for config"
yield f"Processing with {config}"
def wrapper_loop():
# The "shorthand" approach
for item in sub_generator():
yield item
gen = wrapper_loop()
print(next(gen)) # Output: Ready for config
try:
gen.send("High-Priority")
except StopIteration:
pass
# Wait... where did "Processing with High-Priority" go?
In the example above, the wrapper_loop is just iterating. When you call gen.send("High-Priority"), that value is sent to the wrapper_loop, not the sub_generator. The wrapper_loop doesn't know what to do with a sent value, so it just ignores it or crashes. The communication chain is broken.
Building a transparent pipe for bidirectional communication
This is where yield from changes the game. It doesn't just iterate; it establishes a transparent bidirectional pipe between the caller and the sub-generator. Anything you send() or throw() into the main generator is passed directly through to the delegated one.
def sub_generator():
config = yield "Ready for config"
yield f"Processing with {config}"
def wrapper_delegator():
# This creates a direct link to sub_generator
yield from sub_generator()
gen = wrapper_delegator()
print(next(gen)) # Output: Ready for config
print(gen.send("High-Priority")) # Output: Processing with High-Priority
I like to think of yield from as "opening a portal." The wrapper_delegator effectively steps aside and lets the caller talk directly to the sub_generator until it's finished. This is critical for complex asynchronous patterns or state machines where the inner generator needs to react to external inputs.
Capturing the final result of a delegated generator
There is one more "superpower" that a for loop completely lacks: the ability to capture the return value of the sub-generator. In Python, when a generator returns a value (using the return statement), that value is bundled into the StopIteration exception.
A for loop simply ignores this return value. But yield from captures it and allows you to assign it to a variable.
def gather_data():
yield "Chunk 1"
yield "Chunk 2"
return "All chunks processed!"
def master_process():
# yield from returns the value the sub-generator returned
result = yield from gather_data()
print(f"Sub-generator said: {result}")
yield "Master process finished"
for val in master_process():
print(val)
# Output:
# Chunk 1
# Chunk 2
# Sub-generator said: All chunks processed!
# Master process finished
This allows you to build hierarchical generators where the "parent" can make decisions based on the final outcome of a "child" process. It's a clean, elegant way to manage nested logic without having to manually catch StopIteration exceptions and dig through the value attribute.
📋 Practical Task
Exercise: Building a Nested Command Processor with Return Values
You are building a system that processes a sequence of commands. Some commands are "simple" and can be handled by the main generator, while others are "complex" and must be delegated to a specialized handler generator.
Requirements:
- Create a generator called
complex_handler(). It shouldyieldthe string "Processing complex task..." and thenreturnthe integer100(representing a success score). - Create a main generator called
command_processor().- It should first
yieldthe string "Starting processor". - It should then use
yield fromto delegate tocomplex_handler(). - It must capture the return value from
complex_handler(). - Finally, it should
yielda string that says "Complex task completed with score: [score]", using the captured return value.
- It should first
- Write the code to iterate through
command_processor()and print every yielded value.
There are no comments for now.