Lesson 21 of 25

List Comprehensions & Generators

Advanced Comprehensions

We introduced basic list comprehensions earlier. Now let's explore more advanced patterns including nested comprehensions, dictionary comprehensions, set comprehensions, and conditional expressions within comprehensions.

These patterns let you write powerful transformations in a single, readable expression.

Example
# Nested list comprehension — create a matrix
matrix = [[i * j for j in range(1, 5)] for i in range(1, 5)]
for row in matrix:
    print(row)
# [1, 2, 3, 4]
# [2, 4, 6, 8]
# [3, 6, 9, 12]
# [4, 8, 12, 16]

# Conditional expression in comprehension
numbers = range(-5, 6)
labels = [f"{n} is positive" if n > 0 else f"{n} is negative" if n < 0 else "zero" for n in numbers]
print(labels)

# Dictionary comprehension
words = ["hello", "world", "python"]
word_lengths = {word: len(word) for word in words}
print(word_lengths)  # {'hello': 5, 'world': 5, 'python': 6}

# Set comprehension
nums = [1, 1, 2, 2, 3, 3, 4, 4]
unique_squares = {n ** 2 for n in nums}
print(unique_squares)  # {1, 4, 9, 16}

# Flattening nested lists
nested = [[1, 2], [3, 4], [5, 6]]
flat = [item for sublist in nested for item in sublist]
print(flat)  # [1, 2, 3, 4, 5, 6]
  • [expr for item in iterable] — basic list comprehension
  • [expr for item in iterable if condition] — with filter
  • [expr_if_true if condition else expr_if_false for item in iterable] — with conditional expression
  • {key: value for item in iterable} — dictionary comprehension
  • {expr for item in iterable} — set comprehension
Try Advanced Comprehensions
JavaScript
# Multiplication table
table = [[i * j for j in range(1, 6)] for i in range(1, 6)]
for row in table:
    print(row)

print()

# Dict comprehension
word_lengths = {w: len(w) for w in ["hello", "world", "python"]}
print(word_lengths)

# Classify numbers
classified = ["even" if n % 2 == 0 else "odd" for n in range(1, 6)]
print(classified)
Notes
  • Don't overuse comprehensions. If a comprehension becomes too complex to read at a glance, break it into a regular for loop instead.

Generators and yield

A generator is a special type of iterator that generates values lazily — one at a time, on demand — instead of storing them all in memory at once. This makes generators memory-efficient for large datasets.

You create a generator function using the 'yield' keyword instead of 'return'. Each time yield is called, the function's state is paused and the value is produced. Calling next() resumes from where it left off.

Example
# Generator function
def count_up(limit):
    n = 1
    while n <= limit:
        yield n  # Pause and produce a value
        n += 1

# Using the generator
counter = count_up(5)
print(next(counter))  # 1
print(next(counter))  # 2
print(next(counter))  # 3

# Iterating over a generator
for num in count_up(5):
    print(num, end=" ")  # 1 2 3 4 5
print()

# Generator expression (like list comprehension but with parentheses)
squares_gen = (x ** 2 for x in range(1, 1000000))
print(next(squares_gen))  # 1
print(next(squares_gen))  # 4
# Memory efficient! Doesn't create a list of 1 million items

# Practical example — reading large files
def read_large_file(path):
    with open(path, 'r') as f:
        for line in f:
            yield line.strip()

# Chaining generators
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
first_10 = [next(fib) for _ in range(10)]
print(first_10)  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
  • yield — produce a value and pause the function
  • next(generator) — resume the generator and get the next value
  • Generator expressions use () instead of [] — (expr for item in iterable)
  • Generators are single-use: once exhausted, they cannot be restarted
  • Generators are memory-efficient for large or infinite sequences
Try Generators
JavaScript
# Fibonacci generator
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
first_10 = [next(fib) for _ in range(10)]
print("Fibonacci:", first_10)

# Generator expression
squares = (x**2 for x in range(1, 6))
print("Squares:", list(squares))
Notes
  • Use generators when working with large datasets, streaming data, or infinite sequences. They save memory by producing values on demand rather than storing everything at once.