Python · Syntax · Intermediate

List and dict comprehensions

10 tasks

Build lists, dicts, and sets in a single expression. Covers list/dict/set comprehensions and generator expressions.

List, dict, and set comprehensions: syntax, filtering, generator expressions

#
**From loop to comprehension** A list comprehension is a one-line expression that builds a list by transforming and/or filtering an iterable. It replaces a common loop pattern: ```python # Traditional loop squares = [] for x in range(10): squares.append(x ** 2) # List comprehension — same result squares = [x ** 2 for x in range(10)] ``` The general structure is: ``` [expression for item in iterable if condition] ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ what to where to get it optional filter produce ``` **Filtering with if** ```python # Only even squares even_squares = [x ** 2 for x in range(10) if x % 2 == 0] # [0, 4, 16, 36, 64] # Only non-empty strings after stripping words = [' hello ', '', ' ', 'world'] clean = [w.strip() for w in words if w.strip()] # ['hello', 'world'] ``` **Dict and set comprehensions** The same syntax works for dicts and sets: ```python # Dict comprehension word_lengths = {w: len(w) for w in ['hello', 'world', 'python']} # {'hello': 5, 'world': 5, 'python': 6} # Invert a dict (careful: only works if values are unique) original = {'a': 1, 'b': 2, 'c': 3} inverted = {v: k for k, v in original.items()} # {1: 'a', 2: 'b', 3: 'c'} # Set comprehension — automatically deduplicates unique_lengths = {len(w) for w in ['hi', 'hello', 'hey', 'world']} # {2, 5} ``` **Generator expressions** Replace `[...]` with `(...)` to get a lazy generator — values computed on demand, no intermediate list stored in memory: ```python # List — builds the whole list upfront total = sum([x ** 2 for x in range(10_000_000)]) # Generator — computes one value at a time, much less memory total = sum(x ** 2 for x in range(10_000_000)) # When passing to a function, outer parens can be dropped ``` **The walrus operator `:=` in comprehensions (Python 3.8+)** Sometimes you compute an intermediate value for both the filter and the output. `:=` (walrus) lets you assign inside an expression: ```python # Without walrus — compute strip() twice: clean = [w.strip() for w in words if w.strip()] # With walrus — compute once, reuse: clean = [s for w in words if (s := w.strip())] ``` Note: the variable `s` leaks into the enclosing scope after the comprehension — unlike normal comprehension variables, which are scoped to the comprehension itself.

Nested comprehensions, cartesian product, and performance vs loops

#
**Nested comprehensions** A nested comprehension has two `for` clauses. The outer loop runs first, the inner loop runs for each outer iteration — same order as nested `for` loops: ```python # Flatten a 2D matrix matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] flat = [n for row in matrix for n in row] # [1, 2, 3, 4, 5, 6, 7, 8, 9] # Equivalent loop: flat = [] for row in matrix: # outer — first in comprehension for n in row: # inner — second in comprehension flat.append(n) # Cartesian product pairs = [(x, y) for x in [1, 2, 3] for y in ['a', 'b']] # [(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')] ``` **Comprehension of comprehensions** The expression itself can be a comprehension, creating a list of lists: ```python # Transpose a matrix matrix = [[1, 2, 3], [4, 5, 6]] transposed = [[row[i] for row in matrix] for i in range(3)] # [[1, 4], [2, 5], [3, 6]] ``` **Performance: comprehension vs loop** List comprehensions are generally faster than equivalent `for` loops in CPython because the list-building bytecode is optimized: ```python import timeit # Comprehension timeit.timeit('[x*2 for x in range(1000)]', number=10000) # roughly 0.5 s # Equivalent loop timeit.timeit( 'result=[]\nfor x in range(1000): result.append(x*2)', number=10000 ) # roughly 0.8 s — ~60% slower ``` The `map()` function with a simple built-in (no lambda) is the fastest for pure transformation, but the difference is rarely meaningful outside hot loops: ```python list(map(str, range(1000))) # fastest for single built-in function [str(x) for x in range(1000)] # almost as fast, more readable ```

When not to use comprehensions: side effects, nesting limits, common mistakes

#
**When NOT to use comprehensions** Comprehensions are great for simple, declarative transformations. But they have a readability limit — push past it and a regular loop is better. **Rule of thumb**: if you can't read it aloud in one breath, use a loop. ```python # OK — one transformation, one filter result = [x * 2 for x in items if x > 0] # Borderline — two filters, one transform result = [x.strip() for x in lines if x.strip() and not x.startswith('#')] # Too complex — use a loop: # (walrus helps but still hard to read) result = [processed for x in data if (y := expensive(x)) is not None if (processed := transform(y)) > threshold] ``` **Never use comprehensions for side effects** ```python # BAD — comprehension just for the side effect: [print(x) for x in items] # creates a pointless list of Nones [db.save(item) for item in items] # same problem # GOOD — use a loop for side effects: for x in items: print(x) ``` **Common comprehension mistakes** ```python # 1. Forgetting that variables leak with walrus (:=) clean = [s for w in words if (s := w.strip())] print(s) # still defined here — might not be what you expect # 2. Using assignment = instead of comparison == inside filter: # [x for x in items if x = 0] # SyntaxError — good, Python catches this # 3. Deep nesting — hard to read: result = [[cell*2 for cell in row if cell > 0] for row in matrix if sum(row) > 0] # Much clearer as a function with loops # 4. Comprehension over a generator that gets exhausted: gen = (x for x in range(5)) a = [x for x in gen] # [0, 1, 2, 3, 4] b = [x for x in gen] # [] — generator already exhausted! ``` **Quick decision guide** ``` Situation Use ───────────────────────────────── ────────────────────────────── Simple transform or filter list/dict/set comprehension Large dataset, process one at a time generator expression Side effects (print, save, etc.) for loop Complex logic / multiple conditions for loop with if/else Two+ levels of nesting for loops (readability first) ```
01

Even numbers

#

Write a function that takes a list of integers and returns a new list containing only the even numbers. Use a list comprehension.

def even_numbers(numbers):
    pass


result = even_numbers([1, 2, 3, 4, 5, 6, 7, 8])
print(result)
Solution
def even_numbers(numbers):
    return [n for n in numbers if n % 2 == 0]


result = even_numbers([1, 2, 3, 4, 5, 6, 7, 8])
print(result)
02

Squares

#

Write a function that takes a list of numbers and returns a list of their squares. Use a list comprehension.

def squares(numbers):
    pass


result = squares([1, 2, 3, 4, 5])
print(result)
Solution
def squares(numbers):
    return [n ** 2 for n in numbers]


result = squares([1, 2, 3, 4, 5])
print(result)
03

Long words

#

Write a function that takes a list of strings and returns only those strings whose length is greater than a given minimum length.

def long_words(words, min_length):
    pass


result = long_words(["cat", "elephant", "dog", "hippopotamus"], 4)
print(result)
Solution
def long_words(words, min_length):
    return [w for w in words if len(w) > min_length]


result = long_words(["cat", "elephant", "dog", "hippopotamus"], 4)
print(result)
04

Uppercase words

#

Write a function that takes a list of strings and returns a new list with all strings converted to uppercase.

def to_uppercase(words):
    pass


result = to_uppercase(["hello", "world", "python"])
print(result)
Solution
def to_uppercase(words):
    return [w.upper() for w in words]


result = to_uppercase(["hello", "world", "python"])
print(result)
05

Word lengths

#

Write a function that takes a list of words and returns a dictionary where each key is a word and the value is its length.

def word_lengths(words):
    pass


result = word_lengths(["apple", "banana", "fig"])
print(result)
Solution
def word_lengths(words):
    return {w: len(w) for w in words}


result = word_lengths(["apple", "banana", "fig"])
print(result)
06

Flatten a matrix

#

Write a function that takes a list of lists (a matrix) and returns a single flat list containing all the elements.

def flatten(matrix):
    pass


result = flatten([[1, 2, 3], [4, 5], [6, 7, 8, 9]])
print(result)
Solution
def flatten(matrix):
    return [item for row in matrix for item in row]


result = flatten([[1, 2, 3], [4, 5], [6, 7, 8, 9]])
print(result)
07

Unique letters

#

Write a function that takes a string and returns a set of all unique letters in that string, excluding spaces.

def unique_letters(text):
    pass


result = unique_letters("hello world")
print(result)
Solution
def unique_letters(text):
    return {ch for ch in text if ch != " "}


result = unique_letters("hello world")
print(result)
08

Positive and negative

#

Write a function that takes a list of numbers and returns a new list where positive numbers are kept as-is and negative numbers are replaced with their absolute value.

def abs_values(numbers):
    pass


result = abs_values([3, -1, 4, -1, -5, 9, -2, 6])
print(result)
Solution
def abs_values(numbers):
    return [n if n >= 0 else -n for n in numbers]


result = abs_values([3, -1, 4, -1, -5, 9, -2, 6])
print(result)
09

Inverted index

#

Write a function that takes a list of strings and returns a dictionary mapping each string to its index in the list.

def inverted_index(items):
    pass


result = inverted_index(["apple", "banana", "cherry"])
print(result)
Solution
def inverted_index(items):
    return {item: idx for idx, item in enumerate(items)}


result = inverted_index(["apple", "banana", "cherry"])
print(result)
10

Sum of squares with generator

#

Write a function that takes a list of numbers and returns the sum of the squares of all numbers greater than zero. Use a generator expression inside sum().

def sum_of_positive_squares(numbers):
    pass


result = sum_of_positive_squares([1, -2, 3, -4, 5])
print(result)
Solution
def sum_of_positive_squares(numbers):
    return sum(n ** 2 for n in numbers if n > 0)


result = sum_of_positive_squares([1, -2, 3, -4, 5])
print(result)