Data structures · Lesson 9 of 18 · 16 min

Comprehensions

List, set, dict, and generator expressions as a dialect.

  • Rewrite map/filter loops as comprehensions
  • Know when a generator expression is enough
  • Avoid nested comprehension soup

A comprehension is a loop in expression form

`[f(x) for x in xs if p(x)]` builds a list. `{f(x) for x in xs}` builds a set. `{k: v for k, v in pairs}` builds a dict. The `if` is a filter, not an `else`. There is no comprehension ternary besides the ordinary `x if cond else y` in the value position.

A generator expression `(f(x) for x in xs)` is lazy: it produces values as you iterate. `sum(n * n for n in range(1000))` never builds the list of squares. Wrapping in `[]` would.

Four shapes
words = ["Ada", "alan", "Py", "lambda"]
print([w.lower() for w in words if len(w) > 2])
print({w[0].lower() for w in words})
print({w: len(w) for w in words})
gen = (len(w) for w in words)
print(gen, list(gen), list(gen))

Nesting and flattening

Two `for` clauses flatten: `[x for row in matrix for x in row]`. Read them in the same order you would write nested for-loops. If you have to squint, write the loops. Clarity beats cleverness; a comprehension with two filters, a walrus, and a nested call is usually a function.

Dict comprehensions overwrite duplicate keys. `{c: i for i, c in enumerate('abba')}` keeps the last index of each letter.

Flatten and last-wins
matrix = [[1, 2], [3, 4]]
print([x for row in matrix for x in row])
print({c: i for i, c in enumerate("abba")})

Check yourself

Quiz

  1. 1.`(n for n in range(3))` is a:

  2. 2.After `g = (x for x in [1]); list(g); list(g)` the second list is:

  3. 3.Filter in a comprehension uses:

Practice

Exercise

Write `squares(n)` returning a list of squares of 0..n-1, and `odds(xs)` returning a new list of the odd numbers in xs.

Tutor

Ask a question about this lesson. Answers are generated on demand and capped so the session stays focused.