Remember
A generator yields values lazily. yield pauses its execution, and the next request resumes it from that point.
A generator yields values lazily. yield pauses its execution, and the next request resumes it from that point.
Follow the example, change its sample, and compare what happens.
Explain the result and test your understanding.
Focus on how the names, objects, and calculations change. We’ll inspect the printed output in the next section.
Follow the code and watch its state change. Guided simulation.
def countdown(number):
while number > 0:
yield number
number -= 1
values = countdown(3)
print(next(values, "done"))
print(next(values, "done"))Top-level scope
function countdownObject 2 · generator<generator object countdown>A generator yields values lazily. yield pauses its execution, and the next request resumes it from that point.
(no output)
What do you expect this program to print?
EXAMPLE INSTRUCTIONS
def countdown(number):
while number > 0:
yield number
number -= 1
values = countdown(3)
print(next(values, "done"))
print(next(values, "done"))
|
v
DISPLAYED RESULT
3
2
FINAL BINDINGS
countdown --> object 1 (function)
values --> object 2 (generator)
object 1: function countdown
object 2: <generator object countdown>Choose a different sample input, then follow every step through to the output. Before revealing it, predict what the program will display.
Choose a sample and follow its execution. Guided simulation.
def countdown(number):
while number > 0:
yield number
number -= 1
values = countdown(3)
print(next(values, "done"))
print(next(values, "done"))Top-level scope
function countdownObject 2 · generator<generator object countdown>The example has finished. Compare the final output with the values and references above.
3 2
What do you expect this program to print?
def countdown(number):
while number > 0:
yield number
number -= 1
values = countdown(3)
print(next(values, "done"))
print(next(values, "done"))When working with large datasets or continuous data streams, we often seek smarter ways to process information without gobbling up memory. That’s where Python Generators come in — they allow you to iterate lazily, computing values only as needed.
A generator is a special type of iterator in Python. It lets you generate values on the fly instead of storing them in memory like lists. You define a generator using a regular function but with a twist — the yield keyword.
Let’s take a simple example to see generators in action.
def count_up_to(n):
count = 1
while count <= n:
yield count
count += 1
gen = count_up_to(3)
print(gen) # Just a generator object
for num in gen:
print(num)
<generator object count_up_to at 0x...>
1
2
3
Unlike regular functions, calling a generator function doesn't run it. Instead, it returns a generator object. When used in a for loop, it executes until the next yield is hit — pausing there and resuming from the same spot in the next iteration.
Generators don’t compute values until you ask for them. This approach is known as lazy evaluation.
def infinite_numbers():
num = 0
while True:
yield num
num += 1
gen = infinite_numbers()
for _ in range(5):
print(next(gen))
0
1
2
3
4
This example shows how generators can theoretically run forever — only producing the next value when needed.
import sys
def gen_func():
for i in range(1000):
yield i
gen_obj = gen_func()
list_obj = [i for i in range(1000)]
print(sys.getsizeof(gen_obj)) # Lightweight
print(sys.getsizeof(list_obj)) # Much heavier
112
9024
Notice how the generator object is tiny in memory compared to the full list — even though they represent the same range of values.
You can manually control a generator using the next() function.
def greeting():
yield "Hi"
yield "Hello"
yield "Hey"
g = greeting()
print(next(g))
print(next(g))
print(next(g))
Hi
Hello
Hey
If you call next() again after it's exhausted, it will raise a StopIteration exception.
Much like list comprehensions, Python allows you to create generators in a compact form using generator expressions.
gen_exp = (x * x for x in range(5))
for val in gen_exp:
print(val)
0
1
4
9
16
They’re shorter to write and just as memory-efficient.
yield doesn’t return data like return. Use return only when you want to terminate the generator early.from types import GeneratorType
def sample():
yield 1
print(isinstance(sample(), GeneratorType)) # True
True
itertools — many of its tools work seamlessly with generators.Connect what you noticed in the example with the idea below.
A generator yields values lazily. yield pauses its execution, and the next request resumes it from that point.
Calling a generator function immediately calculates all yielded values.
This is the misconception to avoid.Reaching the recap and completing the quizzes are separate milestones.