Skip to Content
Course content

174: Infinite Generators and Lazy Evaluation

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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].

Rating
0 0

There are no comments for now.

to be the first to leave a comment.