Comprehensions

The Course
•
January 8, 2026
•
6 min read

You have written this shape several times already: make an empty list, loop, append. A comprehension says the same thing in one line.

The same result, two ways Python
prices = [4.99, 12.50, 3.25, 8.00]

# The loop you already know:
with_tax = []
for price in prices:
    with_tax.append(round(price * 1.2, 2))
print(with_tax)

# The comprehension:
with_tax = [round(price * 1.2, 2) for price in prices]
print(with_tax)

Read it left to right as English: the rounded price times 1.2, for each price in prices. The expression comes first because the result is what matters.

Filtering

Add if at the end to keep only some items.

Transform and filter Python
numbers = range(1, 21)

evens = [n for n in numbers if n % 2 == 0]
print(evens)

squares_of_odds = [n ** 2 for n in numbers if n % 2 == 1]
print(squares_of_odds)

words = ["apple", "fig", "banana", "kiwi", "cherry"]
long_words = [w.upper() for w in words if len(w) > 4]
print(long_words)

The order is always the same: what you want, for what you loop over, if what you keep.

if in front means something different

[x if x > 0 else 0 for x in values] is a conditional expression choosing the value, so it comes before the for. [x for x in values if x > 0] is a filter, so it comes after. Front means choose, back means keep.

Choose versus keep Python
values = [3, -1, 4, -5, 9]

print([v if v > 0 else 0 for v in values])    # replaces negatives with 0
print([v for v in values if v > 0])           # drops negatives entirely

Dictionary and set comprehensions

The same syntax with braces builds dictionaries and sets.

Beyond lists Python
words = ["apple", "fig", "banana"]

lengths = {word: len(word) for word in words}
print(lengths)

# Invert a dictionary in one line:
capitals = {"France": "Paris", "Japan": "Tokyo"}
print({city: country for country, city in capitals.items()})

# A set comprehension - duplicates collapse:
letters = {letter for word in words for letter in word}
print(sorted(letters))

That last one has two for clauses: they read in the same order you would write them as nested loops — outer first, inner second.

Generator expressions

Swap the brackets for parentheses and nothing is built in memory; values are produced one at a time, as needed.

Lazy by default Python
numbers = range(1, 1_000_001)

# A list of a million squares - all held in memory at once.
squares_list = [n ** 2 for n in numbers]
print(type(squares_list), len(squares_list))

# A generator - nothing computed yet.
squares_gen = (n ** 2 for n in numbers)
print(type(squares_gen))

# sum() consumes it one value at a time, using almost no memory.
print(sum(n ** 2 for n in range(1, 1001)))

When you pass a comprehension straight into sum, max, any or all, drop the brackets — the generator form does the same job without building a throwaway list.

any and all Python
passwords = ["hunter2", "correct-horse", "12345678"]

print(any(len(p) < 8 for p in passwords))       # is any too short?
print(all(len(p) >= 7 for p in passwords))      # are all long enough?
print(sum(1 for p in passwords if p.isdigit())) # count matching items
Readable beats clever

A comprehension should fit on one line and hold one idea. Once you are nesting three for clauses with two conditions, a plain loop is the kinder choice — for the next reader, and for you.

Before you write your own

1. Predict. Four comprehensions, four lines. The second is the “choose versus keep” one.

What will this print? Python
nums = [3, -2, 7, 0, -5]

print([n * 2 for n in nums if n > 0])
print([n if n > 0 else 0 for n in nums])
print({n: n * n for n in range(1, 4)})
print(sum(1 for n in nums if n < 0))
Check your prediction
[6, 14]
[3, 0, 7, 0, 0]
{1: 1, 2: 4, 3: 9}
2

The first keeps only the positives, so it’s shorter than nums. The second chooses a value for every item, so it’s the same length. The last counts: one for each item that passes the filter.

2. Modify. Rewrite this loop as a single comprehension. It should still print [12.5, 45.0, 60.0].

From loop to comprehension Python
expenses = [12.5, 3.0, 45.0, 8.25, 60.0]

big = []
for e in expenses:
    if e > 10:
        big.append(e)

print(big)
Check your change
expenses = [12.5, 3.0, 45.0, 8.25, 60.0]

big = [e for e in expenses if e > 10]

print(big)
[12.5, 45.0, 60.0]

The three parts move across unchanged: what you append (e), what you loop over (for e in expenses) and what you keep (if e > 10).

Exercise

Clean a data file

The list holds raw lines from a form. Produce a list of clean names — stripped of whitespace, title-cased, with blank entries dropped — then a dictionary mapping each name to its length. Your output should be:

['Ada Lovelace', 'Alan Turing', 'Grace Hopper']
{'Ada Lovelace': 12, 'Alan Turing': 11, 'Grace Hopper': 12}
raw = ["  ada lovelace ", "", "ALAN TURING", "   ", "grace hopper"]

# Your code here
Hint 1: the idea

Two comprehensions. The first changes each line — strip it, then title-case it ("ada lovelace".title() is "Ada Lovelace") — and keeps only the lines that aren’t blank. The second builds a dictionary from the clean names. A line is blank if nothing is left after stripping.

Hint 2: a first step

Start with only the tidying: [line.strip().title() for line in raw], and print it. You’ll see two empty strings in the result. Add a filter at the end to drop them — and remember from Day 2 which strings count as false.

Show one solution
raw = ["  ada lovelace ", "", "ALAN TURING", "   ", "grace hopper"]

names = [line.strip().title() for line in raw if line.strip()]
print(names)

lengths = {name: len(name) for name in names}
print(lengths)

if line.strip() uses truthiness from Day 2 — an empty or whitespace-only string is falsy, so those lines are dropped without a length check.

Day 4 is done

Functions and comprehensions are the two habits that most separate Python that reads well from Python that merely runs.

Tomorrow, the world pushes back: errors, files, and the standard library.

What you learned

  • [expression for item in iterable] builds a list in one line.
  • A trailing if filters; a leading if/else chooses a value.
  • {k: v for ...} and {v for ...} build dictionaries and sets.
  • (expression for item in iterable) is a lazy generator — ideal inside sum, any, all.
  • Keep them to one idea; reach for a loop when they stop reading cleanly.
Last updated on January 8, 2026

Was this article helpful?

Your response is saved on this device.