Generators
Generators¶
Generators are custom iterators.
- Generator Function
- Generator Expression
- Reinventing the Parser Generator | YouTube.com David Beazley
Generators are one way to make a function or expression that can be iterated in a loop or with the next()
function.
Generator Functions¶
def counter_gen():
count = 0
while True:
count += 1
yield count
for count in counter_gen():
print(count)
if count >= 10:
break
Generator functions will not remember where they left off from one call to the next.
for count in counter_gen():
print(count)
if count >= 10:
break
But there is a way to make them remember! Memoization. This ability is not unique to generator functions, you can memoize any function.
def smart_counter_gen():
if not hasattr(smart_counter_gen, "count"):
smart_counter_gen.count = 0
while True:
smart_counter_gen.count += 1
yield smart_counter_gen.count
for count in smart_counter_gen():
print(count)
if count >= 10:
break
for count in smart_counter_gen():
print(count)
if count >= 20:
break
What if we wanted a way to reset the counter?
def smart_counter(reset=False):
if reset or not hasattr(smart_counter, "count"):
smart_counter.count = 0
while True:
smart_counter.count += 1
yield smart_counter.count
for count in smart_counter():
print(count)
if count >= 10:
break
for count in smart_counter(reset=True):
print(count)
if count >= 10:
break
for count in smart_counter():
print(count)
if count >= 20:
break
Generator Expressions¶
Generator expressions look a lot like tuple comprehensions, but there is no such thing as a tuple comprehension in Python. Generator expressions can be a little tricky at first because they can only be evaluated once and this can be a subtle problem as there will be no error – it just wont do what you want after the first evaluation.
square_gen = (i*i for i in range(1, 11))
for square in square_gen:
print(square)
for square in square_gen:
print(square) # Prints Nothing! The generator is empty.
Generator Expression that works like a Generator Function¶
To make a generator expression or any iterator that can be used more than once, we must wrap it in a callable, like a lambda. Every time we call this lambda a new generator is created for us. This gives the added ability to parameterize the generator expression.
square_gen = lambda n: (i*i for i in range(n+1))
for square in square_gen(9):
print(square)
for square in square_gen(5):
print(square)