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.
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?