Functions · Lesson 12 of 18 · 18 min

Generators

yield, lazy pipelines, and itertools.

  • Write generator functions with yield
  • Build lazy pipelines
  • Know when to materialize

yield pauses the function

A function with `yield` does not run its body on call. It returns a generator object. Each `next(gen)` (or each step of a for-loop) runs until the next `yield`, hands the value out, and freezes local state. When the function returns, the generator raises `StopIteration`.

This is how you stream data you cannot or should not hold in memory: file lines, network records, infinite sequences. `yield from subgen` delegates to another iterable.

A countdown and a pipeline
def countdown(n):
    while n > 0:
        yield n
        n -= 1

print(list(countdown(4)))

def squares(xs):
    for x in xs:
        yield x * x

def evens(xs):
    for x in xs:
        if x % 2 == 0:
            yield x

print(list(evens(squares(range(8)))))

itertools is the rest of the language

`itertools` is the standard library for lazy iteration: `islice`, `chain`, `cycle`, `repeat`, `count`, `groupby`, `product`, `permutations`, `combinations`. Combine them instead of writing index-heavy loops.

`yield` can also receive values with `gen.send(x)`, which is the foundation of older coroutine style. Modern async uses `async def` / `await` instead; do not mix the two styles without a reason.

islice and chain
from itertools import islice, chain, count

print(list(islice(count(10), 5)))
print(list(chain("ab", [1, 2])))

def take(n, it):
    yield from islice(it, n)

print(list(take(3, countdown(10))))

Check yourself

Quiz

  1. 1.Calling a generator function returns:

  2. 2.`yield from xs` is closest to:

  3. 3.len(countdown(5)) will:

Practice

Exercise

Write a generator `count_from(start)` that yields start, start+1, start+2, ... forever. Write `take(n, it)` that yields the first n items of it.

Tutor

Ask a question about this lesson. Answers are generated on demand and capped so the session stays focused.