List and Dictionary Comprehensions
"For every student in the class, write down their name in capitals." One sentence, and everyone knows exactly what list you'll end up with.
In Lists, you built new lists with a pattern that you'll use constantly: start with an empty list, loop, and append.
scores = [88, 94, 72, 65, 91]
curved = []
for score in scores:
curved.append(score + 5)
print(curved)[93, 99, 77, 70, 96]That's four lines for one idea: "each score, plus 5." Python has a shorter way to say exactly that, called a comprehension:
scores = [88, 94, 72, 65, 91]
curved = [score + 5 for score in scores]
print(curved)[93, 99, 77, 70, 96]Same result, one line, and once you're used to it, it reads almost like the sentence you'd say out loud.
Saying what you want, not how to build it
Imagine asking a classmate for help. You could say: "Take a blank sheet. Look at the first name on the list. Write it in capitals. Look at the next name. Write it in capitals. Keep going until you run out." Or you could just say: "Write down every name on the list, in capitals."
The loop version is the first set of instructions: it describes each step of building the list. A comprehension is the second: it describes the list you want, and Python works out the steps.
Reading a list comprehension
A list comprehension has three parts, inside square brackets:
[ expression for item in collection ]
what to keep where it comes fromRead it as: "the expression, for each item in the collection." Some examples:
names = ["maria", "ben", "carlo"]
print([name.title() for name in names])
print([len(name) for name in names])
print([n * n for n in range(1, 6)])['Maria', 'Ben', 'Carlo']
[5, 3, 5]
[1, 4, 9, 16, 25]A good way to read one you haven't seen before: start in the middle, at the for, to see what's being looped over. Then read the beginning, to see what happens to each item.
Filtering with if
Add an if at the end, and only the items that pass the test are kept:
scores = [88, 94, 72, 65, 91]
passing = [score for score in scores if score >= 75]
print(passing)[88, 94, 91]That's the four-line "build a list of passing scores" loop from Lists, in one line. You can transform and filter at the same time, too:
words = ["apple", "Banana", "cherry", "Avocado"]
a_words = [word.lower() for word in words if word.lower().startswith("a")]
print(a_words)['apple', 'avocado']Choosing between two values
The if at the end filters: it decides which items to keep. To keep every item but change what's written depending on a condition, put a conditional expression, from Making Decisions, at the start:
scores = [88, 94, 72, 65, 91]
results = ["pass" if score >= 75 else "fail" for score in scores]
print(results)['pass', 'pass', 'fail', 'fail', 'pass']Notice the difference: filtering gave a shorter list, but this gives one result for every score.
Dictionary comprehensions
The same idea builds dictionaries. Use curly braces, and write a key: value pair at the start:
names = ["Maria", "Ben", "Carlo"]
name_lengths = {name: len(name) for name in names}
print(name_lengths)
prices = {"adobo": 65, "rice": 15, "juice": 20}
on_sale = {item: price * 0.9 for item, price in prices.items() if price > 18}
print(on_sale){'Maria': 5, 'Ben': 3, 'Carlo': 5}
{'adobo': 58.5, 'juice': 18.0}The second one reads: "each item and its price times 0.9, for each item and price in the menu, if the price is over 18."
Set comprehensions
Curly braces with a single value, not a pair, make a set, from Sets, so duplicates disappear automatically:
words = ["cat", "dog", "car", "door", "cow"]
first_letters = {word[0] for word in words}
print(sorted(first_letters))['c', 'd']When not to use one
Comprehensions are great for simple transformations and filters. They're not a replacement for every loop. Use a regular for loop when:
- you need to do something for each item, like printing or saving, rather than build a collection;
- the logic needs several steps, or an
elif; - the comprehension no longer fits comfortably on one line.
If you have to read a comprehension three times to understand it, write the loop. Clear beats clever, every time.
Try it
This program processes a class's quiz results with comprehensions: curving scores, filtering, labeling, and building a dictionary. Predict every line.
zip(names, scores) pairs up the two lists item by item: ("maria", 88), ("ben", 72), and so on. It's the tidy way to loop through two lists that line up.
Running Python here
Running Python right in the browser is coming to Syntaxia soon. For now, you can read and edit the code here. If you have Python installed on your computer (see Setting Up), save it as main.py and run it with python main.py (or python3 main.py on macOS and Linux).
Check your prediction
['Maria', 'Ben', 'Carlo', 'Dina', 'Eli']
[77, 72]
['B', 'C or below', 'A', 'C or below', 'B']
{'Maria': 88, 'Ben': 72, 'Carlo': 95, 'Dina': 67, 'Eli': 81}
Honor roll: ['Maria', 'Carlo']
Initials: MBCDE
Squares of odd numbers: [1, 9, 25, 49, 81]The second line only curves the scores below 75, so it's shorter than the original list. The third line uses two conditional expressions in a row, which works but is right at the edge of readable. The initials line has no square brackets inside join: a comprehension written straight inside a function call works too, and makes the items one at a time.
Try it yourself
- Write a comprehension that gives the names with more than 3 letters.
- Rewrite the grade-labeling line (the third
print) as a regularforloop withif,elif, andelse. Which version do you find easier to read? - Build a dictionary that maps each number from 1 to 5 to its cube, like
{1: 1, 2: 8, ...}.
Check your understanding
Up next
Real data rarely fits in one flat list. A class has students, each student has several scores, and each score belongs to a subject. Next, you'll put lists inside dictionaries, and dictionaries inside lists, in Nested Data Structures.
