Python Arena
Step 2 of 3

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))
Output
Your ticket: 101
Your ticket: 102
Now serving 103

Keep 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))
Output
red
green
Python stops with
Traceback (most recent call last):
  File "main.py", line 9, in <module>
    print(next(palette))
          ~~~~^^^^^^^^^
StopIteration

You 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")
Output
red
green
No more colors

This 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:

In the Python interpreter
>>> def colors():
...     yield "red"
...     yield "green"
...
>>> palette = colors()
>>> list(palette)
['red', 'green']
>>> list(palette)
[]
>>> list(colors())
['red', 'green']

Your task

Challenge 1 of 3

The generator weekdays gives the names of the work days. Fill in the blanks so that the program takes the first two days one at a time and the rest with a loop, and prints:

First: Mon
Second: Tue
Wed
Thu
Fri

Challenge 2

Write an endless generator function fibonacci() that gives the Fibonacci numbers 1, 1, 2, 3, 5, 8, 13, and so on: after the first two ones, each number is the sum of the two before it. For example,

numbers = fibonacci()
for i in range(7):
    print(next(numbers))

should print:

1
1
2
3
5
8
13

Challenge 3

Write a function take(generator, count) that returns a list of the next count values of the generator it gets. If the generator runs out before that, the list holds the values there were. It's an ordinary function, not a generator: take the values with next, and catch the StopIteration. For example,

numbers = countdown(5)
print(take(numbers, 2))
print(take(numbers, 2))
print(take(numbers, 9))

should print:

[5, 4]
[3, 2]
[1]