3. Generator functions

Avoid materializing large collections when you only need to iterate over them. Use yield to generate values on demand. Generator functions are often more memory-efficient.

Lazy generation also lets callers start consuming results before the full sequence exists, which can simplify streaming-style code. The key improvement is not just memory use but a cleaner separation between producing items and collecting them.

3.1. Don’t do this

1def generate_sequential_numbers(n):
2    nums = []
3    for i in range(n):
4        nums.append(i)
5    return nums
6
7sum(generate_sequential_numbers(10000000))

3.2. Do this

1def generate_sequential_numbers(n):
2    for i in range(n):
3        yield i
4
5sum(generate_sequential_numbers(10000000))
1generate_sequential_numbers = lambda n: (i for i in range(n))
2
3sum(generate_sequential_numbers(10000000))