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.
Generators and yield
A function containing yield is a generator function. Calling it does not run its body; it returns a generator object.
next(g)runs the body until the nextyield, hands back that value, and pauses, keeping all local variables.- The next
next(g)resumes straight after the yield. - When the body finishes,
nextraises StopIteration. A for loop orlist()handles this automatically.
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?
- Memory: a list of a million squares stores a million numbers; a generator stores only its current state.
- Infinite sequences: a
while Truegenerator is fine, because values are only made when requested. - Speed when stopping early: if you only need the first few values, the rest are never calculated.
- A generator expression looks like a list comprehension in round brackets:
(x * x for x in data). - map, filter and zip are lazy too. A generator or map object can only be used once: after it is exhausted, it gives nothing more.
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.
- ```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)) ```
- 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.
Check yourself
- 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']`
- 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`
- 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`
- 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`
- 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.
- 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.
- 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.
Sources
- Pearson Edexcel International Advanced Subsidiary/Advanced Level in Computer Science specification, Issue 1 (March 2026)
- Pearson Edexcel IAL Computer Science Unit 4 Programming Language Subset (PLS)
- Pearson Edexcel IAL Computer Science sample assessment materials (Unit 3 and Unit 4)
Last updated 8 November 2026. Spotted a mistake? Tell us.