The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Create an iteratoriterator = iter(values)View examples
Advance an iteratorvalue = next(iterator)View examples
Use an exhaustion defaultvalue = next(iterator, None)View examples
Consume with a loopfor value in iterable: print(value)View examples
Add indexesfor index, value in enumerate(values, start=1): print(index, value)View examples
Pair iterablesfor name, score in zip(names, scores, strict=True): print(name, score)View examples
Create a lazy expressionsquares = (number * number for number in numbers)View examples
Filter lazilyeven = (number for number in numbers if number % 2 == 0)View examples
Yield a valueyield valueView examples
Delegate iterationyield from iterableView examples
End a generatorreturnView examples
Compose lazy stagesresult = sum(value for value in source if value > 0)View examples
Materialize valuescached = list(generator)View examples
Stop when any value matchesfound = any(value > 100 for value in values)View examples
Take a lazy slicefirst_five = islice(iterable, 5)View examples
Chain iterablescombined = chain(first, second)View examples
Generate a counteridentifiers = count(start=1000, step=10)View examples
Close a generatorgenerator.close()View examples

Iteration separates producing values from consuming them. Use generators for one-pass lazy pipelines, make exhaustion and ordering explicit, and materialize with list only when repeated access or random indexing is actually required.

Step by step

Detailed examples

01

Distinguish reusable iterables from one-pass iterators

iter asks an iterable for an iterator, and next advances it until StopIteration. A list can create a fresh iterator for each loop; a generator is normally its own iterator and is exhausted after one pass. for handles StopIteration automatically. A next default is useful only when that value cannot be confused with real data, otherwise use a unique sentinel.

Advance and exhaust an iterator
values = ['a', 'b']
iterator = iter(values)
print(next(iterator))
print(next(iterator))
print(next(iterator, 'finished'))
print(list(values))
print(list(iterator))
Output
a
b
finished
['a', 'b']
[]
Back to quick reference ↑
02

Add positions and align related inputs

enumerate produces index-value pairs without manual counter mutation. zip advances inputs together and normally stops with the shortest; strict=True is preferable when unequal lengths indicate corrupt or incomplete data. Both return lazy iterators, so inputs are consumed only as the result is consumed.

Validate and number paired values
names = ['Ada', 'Grace', 'Linus']
scores = [91, 95, 88]
for position, (name, score) in enumerate(zip(names, scores, strict=True), start=1):
    print(position, name, score)
Output
1 Ada 91
2 Grace 95
3 Linus 88
Back to quick reference ↑
03

Use generator expressions for one-pass transformations

A generator expression resembles a list comprehension with parentheses but computes each value on demand. It is ideal as the sole argument to sum, any, all, min, max, or a consuming function. Do not wrap it in a list unless you need retained values, repeated iteration, length, or indexing.

Transform and filter without intermediate lists
numbers = range(8)
squares_of_even = (number * number for number in numbers if number % 2 == 0)
print(next(squares_of_even))
print(list(squares_of_even))
print(sum(number * number for number in range(5)))
Output
0
[4, 16, 36]
30
Back to quick reference ↑
04

Suspend function state with yield

Any function containing yield returns a generator object when called; its body begins only when iteration starts. Each yield returns a value and preserves local state for the next resume. yield from delegates to a sub-iterable cleanly. A return without a value ends ordinary consumption, while an explicit return value is carried by StopIteration for advanced delegation use.

Chunk values and delegate each chunk
def chunks(values, size):
    chunk = []
    for value in values:
        chunk.append(value)
        if len(chunk) == size:
            yield tuple(chunk)
            chunk.clear()
    if chunk:
        yield tuple(chunk)

def flattened(groups):
    for group in groups:
        yield from group

print(list(chunks(range(7), 3)))
print(list(flattened([['a', 'b'], ['c']])))
Output
[(0, 1, 2), (3, 4, 5), (6,)]
['a', 'b', 'c']
Back to quick reference ↑
05

Know which operation consumes the pipeline

sum, list, tuple, set, min, max, any, and all consume iterators. any and all short-circuit, which can leave part of an iterator available; list consumes everything remaining. When several consumers need identical values, store a materialized collection or recreate the generator instead of accidentally reusing an exhausted object.

Short-circuit and materialize intentionally
source = iter([2, 4, 7, 8])
found_odd = any(value % 2 for value in source)
print(found_odd)
print(list(source))

positive_total = sum(value for value in [-2, 3, 5] if value > 0)
print(positive_total)
Output
True
[8]
8
Back to quick reference ↑
06

Compose efficient iterator building blocks

itertools provides C-level lazy tools for common patterns. islice takes positions without requiring a sliceable input, chain concatenates streams without copying, and count creates an unbounded sequence that must be paired with a bounded consumer. Avoid materializing an infinite iterator and document where every unbounded pipeline stops.

Bound an infinite sequence and chain results
from itertools import chain, count, islice

identifiers = islice(count(start=1000, step=10), 3)
combined = chain(['header'], (str(value) for value in identifiers), ['footer'])
print(list(combined))
Output
['header', '1000', '1010', '1020', 'footer']
Back to quick reference ↑
07

Put cleanup around the suspended yield

A generator can hold files, locks, or other resources across a yield. try/finally in the generator performs cleanup when it finishes, raises, or receives close, but callers should not depend on garbage collection timing. Prefer a context manager that owns the full consumption when prompt cleanup is required, or explicitly close a partially consumed generator.

Observe cleanup on close
def stream():
    try:
        yield 'first'
        yield 'second'
    finally:
        print('closed')

generator = stream()
print(next(generator))
generator.close()
Output
first
closed
Back to quick reference ↑

Local code tester

Try Python iterators and generators

Edit a lazy data pipeline and observe when each generator produces and consumes values.

Runs in your browser
Output
Press Run to load Python locally.

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. Python Software FoundationPython Data Model: Iterator typesdocs.python.org
  2. Python Software FoundationPython Language Reference: Generator expressions and yielddocs.python.org
  3. Python Software FoundationFunctional Programming HOWTO: Iterators and generatorsdocs.python.org
  4. Python Software Foundationitertools — Functions creating iteratorsdocs.python.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback