yield
A def containing yield returns a generator instead of running. Each next() runs the body up to the following yield — lazily, and exactly once per value.
yield
def gen(): yield value
yield from
def gen(): yield from iterable
receive with send()
received = yield value
Use for
Streams of values, big or infinite sequences, pipelines over files and data
Result
calling the function returns a generator object; yield evaluates to what send() passed (None for next())
Pairs with
next(), for, list(), itertools, yield from, return
Watch out
a generator can be iterated only once — the second pass is empty
Demo
Live evaluation
Ask for one value and look at the log: only one item was made, no matter how big n is.
Try:
Inputs
ninthow many to offer
Code
log = [] def numbers(n): for i in range(n): log.append(f'made {i}') yield i g = numbers(5) first = next(g) (first, log)
Result
(0, ['made 0'])
In the lazy tab the log has a single entry even for a billion — the rest were never computed. With n = 0 the generator has nothing to give, so next() raises StopIteration. In the consumed once tab, the second list() is always empty: generators do not rewind.
Syntax slots
| Name | Type | Required | Description |
|---|---|---|---|
| expression | expression | no (None) | The value handed to the consumer of this step. Omitted → None. |
| iterable | expression | yes | yield from only: every item is passed through, and the expression evaluates to the sub-generator’s return value. |
Common patterns
Stream a file line by line
Constant memory, however big the file.
def non_blank(path): with open(path) as f: for line in f: if line.strip(): yield line.rstrip("\n")
Pipeline of generators
Each stage pulls from the previous one, one item at a time.
def parse(lines): for line in lines: yield line.split(",") rows = parse(non_blank("data.csv"))
Flatten with yield from
Delegate to a sub-iterable instead of looping and yielding.
def flatten(tree): for node in tree: if isinstance(node, list): yield from flatten(node) else: yield node
Generator expression
The one-line form for simple cases.
total = sum(x * x for x in numbers)
Examples
1. A simple generator
def count_up(n):
i = 1
while i <= n:
yield i
i += 1
list(count_up(3))
Returns
[1, 2, 3]2. Calling it returns a generator
def gen():
yield 1
type(gen()).__name__
Returns
'generator'3. The body starts at the first next()
def gen():
print('started')
yield 1
g = gen()
print('created')
next(g)
Returns
created
started
14. Infinite, but lazy
from itertools import islice
def naturals():
n = 0
while True:
yield n
n += 1
list(islice(naturals(), 5))
Returns
[0, 1, 2, 3, 4]5. yield from flattens
def flat(lists):
for lst in lists:
yield from lst
list(flat([[1, 2], [3]]))
Returns
[1, 2, 3]6. send() a value in
def echo():
got = yield 'ready'
yield f'got {got}'
g = echo()
(next(g), g.send(42))
Returns
('ready', 'got 42')7. yield outside a function
compile('yield 1', '<demo>', 'exec')
Returns
SyntaxError: 'yield' outside functionPitfalls
1. Iterating a generator twice
sum() consumed every value; the generator is now exhausted. Store a list if you need the values again.
reuse the generator
def evens(xs): for x in xs: if x % 2 == 0: yield x e = evens([1, 2, 3, 4]) total = sum(e) (total, list(e))
(6, [])
materialise once
def evens(xs): for x in xs: if x % 2 == 0: yield x e = list(evens([1, 2, 3, 4])) total = sum(e) (total, list(e))
(6, [2, 4])
2. Treating a generator like a list
Generators have no length and no indexing — they only know how to produce the next value.
len(generator)
def gen(): yield 1 yield 2 len(gen())
TypeError: object of type 'generator' has no len()
list() first
def gen(): yield 1 yield 2 len(list(gen()))
2
3. send() before the generator started
A fresh generator is not paused at a yield yet, so there is nothing to receive the value. Prime it with next() first.
send first
def gen(): x = yield yield x * 2 g = gen() g.send(5)
TypeError: can't send non-None value to a just-started generator
next, then send
def gen(): x = yield yield x * 2 g = gen() next(g) g.send(5)
10
When to use
Use it
- Producing a long or unbounded series without building it in memory
- Reading files, sockets or paginated APIs item by item
- Splitting a loop into stages (produce / filter / transform) that each stay simple
Reach for something else
- You need len(), indexing or several passes → return a list
- The values are small and few → a list is simpler to debug
- A one-line transformation → a generator expression (x for x in …)
Notes
CPython impl
A function containing yield is compiled with the generator flag; calling it creates a generator object holding a suspended frame
Return
return value in a generator ends it and sets StopIteration.value — yield from evaluates to it
yield from
Added in 3.3 (PEP 380); it also forwards send() and throw() to the sub-generator
FAQ
return ends the function and hands back one value. yield hands back a value and pauses; the function resumes where it left off on the next next() call. A function that contains yield anywhere becomes a generator function.
History
3.3
Added yield from <expr> to delegate control flow to a subiterator.
3.8
Yield expressions prohibited in the implicitly nested scopes used to implement comprehensions and generator expressions.
3.13
If a generator returns a value upon being closed, the value is returned by close().