Python · Lesson 8 of 15
Comprehensions and Iteration
Build lists, dicts and sets in one line, and loop smarter with enumerate, zip and sorted.
- Intermediate
- 15 min read
- 3 objectives
Before this lessonLesson 7: Dictionaries and Sets
What you will learn
- Write list, dict and set comprehensions
- Loop with enumerate and zip
- Sort with a key function
Much of everyday Python is take a collection, change or filter it, and get a new collection. Comprehensions express that in a single readable line. Alongside them, a few built-in helpers such as enumerate, zip and sorted remove most of the index bookkeeping you would otherwise write by hand.
From loop to list comprehension
Here is the loop you already know: build an empty list, loop, append. A list comprehension packs the same idea into brackets, in the shape [expression for item in iterable].
nums = [1, 2, 3, 4, 5]
squares = []
for n in nums:
squares.append(n * n)
print(squares)
squares2 = [n * n for n in nums]
print(squares2 == squares)[1, 4, 9, 16, 25] True
Filtering with a condition
Add if at the end to keep only some items. To transform items differently depending on a condition, put a conditional expression at the front instead: [a if test else b for ...].
nums = range(1, 11)
evens = [n for n in nums if n % 2 == 0]
labels = ["even" if n % 2 == 0 else "odd" for n in nums]
print(evens)
print(labels[:4])[2, 4, 6, 8, 10] ['odd', 'even', 'odd', 'even']
Dictionary and set comprehensions
The same syntax works with braces. Use {key: value for ...} for a dict and {item for ...} for a set (which also removes duplicates).
words = ["apple", "kiwi", "banana", "kiwi"]
lengths = {w: len(w) for w in words}
first_letters = {w[0] for w in words}
print(lengths)
print(sorted(first_letters)){'apple': 5, 'kiwi': 4, 'banana': 6}
['a', 'b', 'k']enumerate: the index without the bookkeeping
When you need both the position and the value, do not track a counter yourself. enumerate hands you both, and start= sets the first number.
tasks = ["write", "test", "ship"]
for number, task in enumerate(tasks, start=1):
print(f"{number}. {task}")1. write 2. test 3. ship
zip: walking several lists together
zip pairs up items from two or more sequences and stops at the shortest one. Wrapping the result in dict() is a neat way to build a lookup table from two lists.
names = ["Ada", "Grace", "Linus"]
scores = [95, 88, 91]
for name, score in zip(names, scores):
print(name, score)
print(dict(zip(names, scores)))Ada 95
Grace 88
Linus 91
{'Ada': 95, 'Grace': 88, 'Linus': 91}Sorting with a key
sorted() returns a new sorted list and accepts a key= function that says what to sort by. Use reverse=True for descending order. Python's sort is stable, so equal items keep their original order. Related helpers any() and all() answer "is at least one / is every" questions.
people = [("Ann", 31), ("Bob", 25), ("Cy", 31)]
print(sorted(people, key=lambda p: p[1]))
print(sorted(people, key=lambda p: (-p[1], p[0])))
scores = [72, 88, 95]
print(any(s >= 90 for s in scores), all(s >= 70 for s in scores))[('Bob', 25), ('Ann', 31), ('Cy', 31)]
[('Ann', 31), ('Cy', 31), ('Bob', 25)]
True TrueGenerator expressions
Swap the brackets for parentheses and you get a generator expression: it produces values one at a time instead of building the whole list in memory. It is ideal when you only need to feed the result into sum(), max() or any().
total = sum(n * n for n in range(1, 1001))
print(total)333833500
Common mistakes
- Using a comprehension only for its side effects, like
[print(x) for x in items]. Use a plain loop. - Forgetting that
zipsilently drops extra items when the lists differ in length. - Reusing a generator: once exhausted it yields nothing more. Recreate it or use a list.
# Write your solution here
