Build note
Python yield, One next() at a Time
Follow a three-number example to see how yield pauses a Python generator, how next() resumes it, and how iteration ends.
yield lets a function hand a value back to its caller and pause. When the caller asks for another value, execution resumes where it paused.
A function defined with def that contains yield is a generator function. Calling it creates a generator object, which produces values as you request them.
Start with the output
Run this example with Python 3:
def generate_numbers():
print("Preparing 10")
yield 10
print("Preparing 20")
yield 20
print("Preparing 30")
yield 30
numbers = generate_numbers()
print("Generator created")
print("Received:", next(numbers))
print("Received:", next(numbers))
print("Received:", next(numbers))
It prints:
Generator created
Preparing 10
Received: 10
Preparing 20
Received: 20
Preparing 30
Received: 30
Notice that Generator created comes first. None of the function body has run when generate_numbers() returns the generator.
One request at a time
Follow the same numbers object through the program:
- Creation:
numbers = generate_numbers()creates the generator. NoPreparingmessage appears yet. - First
next(numbers): execution starts at the top of the function. It printsPreparing 10, reachesyield 10, and pauses. The call tonext()returns10, so the outerprint()printsReceived: 10. - Second
next(numbers): execution continues afteryield 10. It printsPreparing 20, yields20, and pauses again. - Third
next(numbers): execution continues afteryield 20. It printsPreparing 30, yields30, and pauses there.
Python evaluates next(numbers) before the surrounding print() can print its arguments. That is why each Preparing line appears before its corresponding Received line.
The generator remembers its position and local variables between requests. It resumes the same execution rather than starting the function again. These are the suspension rules described in Python’s yield reference.
How is yield different from return?
return ends the current function execution. In an ordinary function, return 10 gives the caller 10 and finishes that call.
yield 10 gives the caller of next() a value while preserving the generator’s execution so it can continue later. A return inside a generator finishes it; it does not yield another item.
The pause does not start a background task. In this example, no more preparation happens until you ask for another value.
What happens on the fourth next()?
After the third call, the generator is paused at yield 30. Append this line to the original example:
next(numbers)
Execution resumes, reaches the end of the function, and raises StopIteration. That exception signals that there are no more items. Further calls on the exhausted generator also raise StopIteration; they do not restart it.
If you want a fallback value instead of that exception, use the optional second argument to next():
print(next(numbers, "Done"))
Placed immediately after the original example, this prints Done.
Let a for loop request the values
Usually, a for loop handles the requests and stops automatically when the generator is exhausted. Keep the function definition and replace the code below it with:
for number in generate_numbers():
print("Received:", number)
This creates a fresh generator and prints the same alternating Preparing and Received lines, without Generator created. Calling generate_numbers() again gives you a new sequence to consume.
Generators are useful when you want to process values as they become available without first collecting every result in a list. That can save memory for large sequences, although the generator and the consumer still use memory. The three numbers here make the timing visible: each request advances execution only as far as the next value.