All subjects · Computer Science · WCP04 Advanced Practical Programming and Problem-solving

Lazy evaluation and generators

Edexcel IAL Computer Science · WCP04 Advanced Practical Programming and Problem-solving

Lazy evaluation means a value is calculated only when it is actually needed. In Python, generators produce values one at a time on demand, so they can describe huge or even infinite sequences using almost no memory. The Unit 4 sample paper asks for a lazily evaluated generator.

Practise this in the app: instant marking and a fix for every mistake. Free, no email needed to start.

Practise this lesson

Generators and yield

A function containing yield is a generator function. Calling it does not run its body; it returns a generator object.

def countdown(n):
    while n > 0:
        yield n
        n = n - 1

g = countdown(3)
print(next(g), next(g), next(g))
try:
    next(g)
except StopIteration:
    print("finished")
print(list(countdown(4)))

Output:

3 2 1
finished
[4, 3, 2, 1]

Why lazy?

def multiples_of(k):
    n = k
    while True:            # infinite, but only computed on demand
        yield n
        n = n + k

sevens = multiples_of(7)
print([next(sevens) for _ in range(5)])
squares = (x * x for x in range(1, 1000001))   # generator expression: nothing computed yet
print(next(squares), next(squares), sum(x for x in range(5)))
lazy = map(lambda x: x * 2, [1, 2, 3])
print(type(lazy).__name__, list(lazy), list(lazy))

Output:

[7, 14, 21, 28, 35]
1 4 10
map [2, 4, 6] []

Eager or lazy?

Use a list (eager) when you need to index the values, use them more than once or know their length. Use a generator (lazy) for a long or endless stream that you process once, item by item.

Worked example

Write a generator that lazily yields the even numbers from a list of readings, and take only the first two.

  1. ```python def evens(values): for v in values: if v % 2 == 0: print("checked", v) yield v g = evens([3, 8, 5, 12, 6, 10]) print(next(g)) print(next(g)) ```
  2. Output: ``` checked 8 8 checked 12 12 ```

Answer: Only the values up to 12 are examined; 6 and 10 are never processed because they were never requested.

Key idea: yield makes a generator; next() runs to the next yield and pauses; StopIteration when finished; generator expressions use round brackets; values are computed on demand, so memory stays small and infinite sequences are possible; a generator can only be used once.

Check yourself

  1. What does this program print? ```python def letters(): yield "a" yield "b" yield "c" g = letters() next(g) print(next(g), list(g)) ```
    Show answer

    b ['c']. The first next takes 'a'; the second returns 'b'; list(g) collects what remains. Output: `b ['c']`

  2. What does this program print? ```python g = (n * 3 for n in range(4)) print(sum(g), sum(g)) ```
    Show answer

    18 0. The first sum uses up the generator (0 + 3 + 6 + 9 = 18); the second finds nothing left, so it is 0. Output: `18 0`

  3. What does this program print? ```python def naturals(): n = 1 while True: yield n n = n + 1 gen = naturals() print([next(gen) for _ in range(3)], next(gen)) ```
    Show answer

    [1, 2, 3] 4. The infinite generator makes only the values requested: 1, 2, 3, then 4. Output: `[1, 2, 3] 4`

  4. What does this program print? ```python def noisy(): for i in range(3): print("make", i, end="; ") yield i first = next(noisy()) print("got", first) ```
    Show answer

    make 0; got 0. Only the first value is produced, so 'make' is printed once before 'got 0'. Output: `make 0; got 0`

  5. Which exception does this program raise? ```python def one(): yield 1 g = one() next(g) next(g) ```
    • IndexError
    • ValueError
    • RecursionError
    • StopIteration
    Show answer

    StopIteration. After the only yield, the generator finishes, so the second next raises StopIteration.

  6. Why is `(x * x for x in range(10000000))` better than `[x * x for x in range(10000000)]` when the values are only summed once?
    • The generator gives a more accurate sum
    • The generator produces each value when needed instead of storing ten million values
    • The list cannot be summed
    • Generators can be indexed like lists
    Show answer

    The generator produces each value when needed instead of storing ten million values. Lazy evaluation keeps only the current value in memory.

  7. What turns an ordinary Python function into a generator function?
    • Using return twice
    • Naming it with gen_ at the start
    • Calling it with next() instead of brackets
    • Using the keyword yield in its body
    Show answer

    Using the keyword yield in its body. Any function containing yield returns a generator when called.

Practise with instant feedback

Sources

Last updated 8 November 2026. Spotted a mistake? Tell us.