CollectionsChapter 30 of 114
List Comprehension
Build a list from another one in a single readable line.
The shape
A comprehension builds a new list by walking an existing sequence. This loop:
squares = []
for n in range(5):
squares.append(n ** 2)
print(squares)Output
[0, 1, 4, 9, 16]
becomes one line:
squares = [n ** 2 for n in range(5)]
print(squares)Output
[0, 1, 4, 9, 16]
Read it left to right: what to collect, then where it comes from.
Filtering
An if on the end keeps only some items:
numbers = [1, 2, 3, 4, 5, 6]
evens = [n for n in numbers if n % 2 == 0]
print(evens)Output
[2, 4, 6]
This is the idiomatic way to remove items from a list — build the list you want rather than deleting from the one you have.
Transforming and filtering together
words = [" Ada ", "grace", "", " Katherine"]
cleaned = [w.strip().title() for w in words if w.strip()]
print(cleaned)Output
['Ada', 'Grace', 'Katherine']
if/else goes in front
A filtering if goes at the end. A choosing if/else is part of the expression, so it goes at the front:
numbers = [1, 2, 3, 4]
labels = ["even" if n % 2 == 0 else "odd" for n in numbers]
print(labels)Output
['odd', 'even', 'odd', 'even']
Nesting
Two for clauses read in the same order as the nested loops they replace:
grid = [[1, 2], [3, 4]]
flat = [value for row in grid for value in row]
print(flat)Output
[1, 2, 3, 4]
Building a grid puts the inner comprehension in the expression slot:
table = [[row * col for col in range(1, 4)] for row in range(1, 4)]
print(table)Output
[[1, 2, 3], [2, 4, 6], [3, 6, 9]]
Set and dict comprehensions
The same shape with different brackets:
words = ["ada", "grace", "ada"]
print(sorted({w for w in words}))
print({w: len(w) for w in words})Output
['ada', 'grace']
{'ada': 3, 'grace': 5}Generator expressions
Round brackets give a generator, which produces values one at a time instead of building the whole list. For a big input that saves the memory:
numbers = range(1, 1000001)
print(sum(n for n in numbers if n % 3 == 0))Output
166666833333
If you only need to feed a result into sum(), any(), max() or a for, the generator is the better choice.
Know when to stop
A comprehension should stay readable at a glance. When it needs two filters, a nested condition and a comment, write the loop:
rows = [("Ada", 36), ("Grace", 45)]
labels = []
for name, age in rows:
if age > 40:
labels.append(f"{name} is over forty")
print(labels)Output
['Grace is over forty']
Test yourself
2 questionsWhere does a filtering if go in a comprehension?
Show the answer
At the end, after the for — A filtering if goes last. An if/else that chooses a value goes at the front, because it is part of the expression.
What does (n for n in numbers) give, with round brackets?
Show the answer
A generator that produces values one at a time — It never builds the whole sequence, which is what makes it cheap for large inputs.
Sort Lists
sort in place or sorted into a new list, with a key and a direction.