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
174: Infinite Generators and Lazy Evaluation
Wait, if it's infinite, won't it just freeze my program?
It sounds like a recipe for a crash, right? Normally, a while True loop is something you only see in a main event loop or a server that's meant to run forever. But when you add the yield keyword, the rules change. You aren't creating a loop that runs until it finishes; you're creating a generator object.
The generator doesn't execute its code the moment you call the function. Instead, it pauses. It says, "I'm ready to give you a value whenever you ask for it." It only does the work required to reach the next yield statement, then it freezes its state and waits. I've seen a lot of beginners try to cast an infinite generator to a list using list(my_generator)βdon't do that. That's how you run out of RAM and freeze your machine, because you're telling Python to exhaust an infinite sequence into a finite piece of memory.
def count_forever(start=0):
while True:
yield start
start += 1
# This doesn't freeze; it just creates the generator object
counter = count_forever()
print(next(counter)) # 0
print(next(counter)) # 1
# The function is now "paused" at the yield line until we call next() again.
What's the actual benefit of "lazy evaluation" over just making a large list?
Lazy evaluation is essentially the "just-in-time" delivery of the programming world. The primary benefit is memory efficiency. If you're processing a dataset with ten billion entries, you physically cannot load that into a Python list. But you can easily write a generator that reads one line from a file at a time.
Beyond memory, it also allows you to model streams of data that truly have no defined end. Think about a sensor reading from a thermometer or a live feed of stock prices. You can't put "the future" into a list. By using a generator, you treat that stream as a sequence that you can iterate over, even though the end doesn't exist. It keeps your code clean because you can use the same for loop logic for a small list as you would for a cosmic-scale stream of data.
How do I actually extract a specific amount of data from something that never ends?
Since you can't use slicing (like gen[:10]) on a generator, you need a way to tell the generator to stop once you've had your fill. The most common way is a break statement inside a loop, but if you want to keep your code functional and clean, I highly recommend itertools.islice.
islice allows you to treat a generator almost like a list, letting you specify a start, stop, and step. It consumes the generator only up to the point you specify and then stops, leaving the generator ready to resume if you need more later. It's an incredibly powerful tool for handling infinite streams without accidentally triggering an infinite loop.
from itertools import islice
def fibonacci_gen():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# I only want the first 10 Fibonacci numbers
# islice(iterable, stop)
first_ten = list(islice(fibonacci_gen(), 10))
print(first_ten)
# Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
π Practical Task
Build a Circular Sequence ID Generator
In some distributed systems, you need to assign IDs that cycle through a specific range (e.g., 0 to 4) repeatedly to distribute load across a fixed number of servers. This is called round-robin scheduling.
Your Task:
Write an infinite generator function called round_robin_ids(limit) that takes an integer limit and yields numbers from 0 up to limit - 1. Once it reaches the limit, it should start over at 0 and continue forever.
To test your implementation, use itertools.islice to capture the first 15 IDs from a round_robin_ids(5) generator and print them as a list. The output should be: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4].
There are no comments for now.