next and StopIteration
A for loop takes every value of a generator. Sometimes you want one value at a time, at different places in your program. The built-in function next asks a generator for its next value:
def ticket_numbers(first):
number = first
while True:
yield number
number += 1
tickets = ticket_numbers(101)
print("Your ticket:", next(tickets))
print("Your ticket:", next(tickets))
print("Now serving", next(tickets))
Your ticket: 101
Your ticket: 102
Now serving 103Keep the generator in a variable, as tickets above: each next(tickets) continues that same generator. Calling ticket_numbers(101) again makes a new generator, which starts from 101 again. Two generators made by the same function don't affect each other.
When the values run out
A generator that has ended has nothing more to give. Then next raises a StopIteration exception:
def colors():
yield "red"
yield "green"
palette = colors()
print(next(palette))
print(next(palette))
print(next(palette))
red
greenTraceback (most recent call last):
File "main.py", line 9, in <module>
print(next(palette))
~~~~^^^^^^^^^
StopIterationYou can catch it like any other exception, with try and except:
def colors():
yield "red"
yield "green"
palette = colors()
try:
while True:
print(next(palette))
except StopIteration:
print("No more colors")
red
green
No more colorsThis is also how a for loop knows when to stop. It calls next again and again and ends quietly at the StopIteration. It's the same signal the __next__ method of your own iterator classes raised in part 10.
Used up for good
A finished generator stays finished. Going through it a second time gives nothing, and next raises StopIteration again. To get the values again, make a new generator by calling the function again:
>>> def colors():
... yield "red"
... yield "green"
...
>>> palette = colors()
>>> list(palette)
['red', 'green']
>>> list(palette)
[]
>>> list(colors())
['red', 'green']