Python

List comprehensions — the basics

A more compact way to write the extremely common "build a new list by transforming or filtering another one" loop — worth learning early since it shows up everywhere in real Python code.

Beginner

3 min read

The loop pattern this replaces

squares = []
for n in range(1, 6):
    squares.append(n * n)
 
squares   # [1, 4, 9, 16, 25]

Building a new list by doing something to each item of another sequence is extremely common — common enough that Python has a dedicated, more compact syntax for exactly this shape, called a list comprehension:

squares = [n * n for n in range(1, 6)]
squares   # [1, 4, 9, 16, 25]

Both versions do exactly the same thing. The comprehension reads left to right almost like English: "n times n, for each n in range(1, 6)." It's not a different feature — it's the same loop-and-append pattern, written more compactly, and it's idiomatic enough in real Python code that recognizing it on sight is worth the small amount of practice it takes.

The general shape

[EXPRESSION for ITEM in ITERABLE]

ITERABLE is anything you can loop over (a list, a string, range(), and more). ITEM is the name given to each element as the comprehension walks through it. EXPRESSION is what gets computed for each item and placed into the new list — it can be as simple as item itself, or any transformation of it.

names = ["ada", "grace", "alan"]
capitalized = [name.capitalize() for name in names]
capitalized   # ["Ada", "Grace", "Alan"]

Adding a filter: only include items that match a condition

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = [n for n in numbers if n % 2 == 0]
evens   # [2, 4, 6, 8, 10]

Adding if CONDITION at the end filters which items make it into the result — this replaces the loop-plus-if-plus-append pattern:

evens = []
for n in numbers:
    if n % 2 == 0:
        evens.append(n)

Both produce the identical list; the comprehension just says it in one line instead of four.

Comprehensions exist for dicts and sets too

words = ["apple", "banana", "cherry"]
lengths = {word: len(word) for word in words}
lengths   # {"apple": 5, "banana": 6, "cherry": 6}
 
unique_lengths = {len(word) for word in words}
unique_lengths   # {5, 6}

{key: value for item in iterable} is a dict comprehension — same idea, building a dictionary instead of a list. {expression for item in iterable} (no colon) is a set comprehension, automatically deduplicating whatever values the expression produces, exactly the way any other set does.

When a comprehension is the wrong choice

# hard to read — too much logic crammed into one line
result = [x * 2 if x % 2 == 0 else x * 3 for x in range(20) if x > 5 and x < 15]
 
# clearer as a plain loop
result = []
for x in range(20):
    if x > 5 and x < 15:
        if x % 2 == 0:
            result.append(x * 2)
        else:
            result.append(x * 3)

Comprehensions are meant to make simple "transform and/or filter" loops more compact, not to cram arbitrarily complex logic into one line. Once a comprehension needs nested conditions, multiple if/else branches, or is hard to read at a glance, a plain for loop is the better, clearer choice — readability, not "did I use the compact syntax," is the actual goal.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What does `[n * n for n in range(1, 4)]` produce?

2. What does adding `if n % 2 == 0` to the end of a list comprehension do?

3. What does `{word: len(word) for word in words}` build?

4. When is a plain for loop generally the better choice over a list comprehension?