A generator is a function that can pause and later carry on.
Calling a generator function gives you back a generator object, and this object is an iterator.
None of its code has run yet. It only starts when you iterate over the generator.
Example
A basic generator function:
Output: complete Python program
def my_generator():
yield 1
yield 2
yield 3
for value in my_generator():
print(value)
The yield Keyword
What turns a function into a generator is the yield keyword.
On reaching a yield, the function hands back a value and saves its state. When the generator is asked for another value, it picks up right where it stopped.
return ends a function for good, but yield only pauses it, so a function can use it many times.
Example
A generator yielding a series of numbers:
Output: complete Python program
def count_up_to(n):
count = 1
while count <= n:
yield count
count += 1
for num in count_up_to(5):
print(num)
Generators Save Memory
A generator produces values as needed instead of keeping them all in memory.
That makes a real difference with large datasets:
Example
A generator for a very long sequence:
Output: complete Python program
def large_sequence(n):
for i in range(n):
yield i
# This doesn't create a million numbers in memory
gen = large_sequence(1000000)
print(next(gen))
print(next(gen))
print(next(gen))
Using next() with Generators
The next() function lets you step through a generator by hand.
Once all the values have been yielded, another call raises a StopIteration exception.
Example
Output: complete Python program
def simple_gen():
yield "Emil"
yield "Tobias"
yield "Linus"
gen = simple_gen()
print(next(gen))
print(next(gen))
print(next(gen))
Example
Output: complete Python program
def simple_gen():
yield 1
yield 2
gen = simple_gen()
print(next(gen))
print(next(gen))
print(next(gen)) # This will raise StopIteration
Generator Expressions
Generator expressions create generators with syntax close to list comprehensions. The only difference is parentheses in place of square brackets:
Example
A list comprehension next to a generator expression:
Output: complete Python program
# List comprehension - creates a list
list_comp = [x * x for x in range(5)]
print(list_comp)
# Generator expression - creates a generator
gen_exp = (x * x for x in range(5))
print(gen_exp)
print(list(gen_exp))
Example
Passing a generator expression to sum:
Output: complete Python program
# Calculate sum of squares without creating a list
total = sum(x * x for x in range(10))
print(total)
Fibonacci Sequence Generator
The Fibonacci sequence is a natural fit for a generator.
It can go on producing values indefinitely without exhausting your memory:
Example
Print the first 100 Fibonacci numbers:
Output: complete Python program
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Get first 100 Fibonacci numbers
gen = fibonacci()
for _ in range(100):
print(next(gen))
Generator Methods
For more advanced control, generators come with some special methods:
Example
With the send() method, you can pass a value into the generator:
Output: complete Python program
def echo_generator():
while True:
received = yield
print("Received:", received)
gen = echo_generator()
next(gen) # Prime the generator
gen.send("Hello")
gen.send("World")
Example
The close() method shuts the generator down:
Output: complete Python program
def my_gen():
try:
yield 1
yield 2
yield 3
finally:
print("Generator closed")
gen = my_gen()
print(next(gen))
gen.close()