Python List Comprehensions
Learn Python list comprehensions: build a new list in one readable line, add an if filter, transform items, and know when a comprehension is clearer than a loop. Runnable code, a worked example and a quiz.
Key takeaways
- A list comprehension builds a new list in one line: [expression for item in iterable]
- It is a compact, readable replacement for a for-loop that appends to a list
- Add an if at the end to filter, keeping only items that match a condition
- The expression can transform each item β square it, uppercase it, or anything else
- Reach for a plain loop instead when the logic is long or has side effects
A shorter way to build a list
A very common task in programming is to take one list and build a new list from it β doubling every number, uppercasing every word, or keeping only the items that pass a test. With the for loops you already know, that looks like this:
numbers = [1, 2, 3, 4]
squares = []
for n in numbers:
squares.append(n * n)
print(squares) # [1, 4, 9, 16]
This works perfectly, but it takes four lines to express a single idea: "square each number". Python offers a more compact and readable tool for exactly this pattern β the list comprehension.
The basic shape
A list comprehension builds a whole list in one line, wrapped in square brackets:
numbers = [1, 2, 3, 4]
squares = [n * n for n in numbers]
print(squares) # [1, 4, 9, 16]
Read it from the middle outwards:
for n in numbersβ loop over each item, calling itn(exactly like a normal for-loop).n * nβ the expression at the front: what to put in the new list for eachn.- The square brackets
[...]collect every result into a new list.
The mental template is:
[ expression for item in iterable ]
Both versions produce the same [1, 4, 9, 16]. The comprehension just says it in one readable line.
Transforming items
The expression at the front can be anything β a calculation, a method call, a function call. Here we uppercase a list of words:
words = ["hello", "world", "python"]
shouted = [w.upper() for w in words]
print(shouted) # ['HELLO', 'WORLD', 'PYTHON']
- For each word
w, the expressionw.upper()produces the uppercase version. - The results are collected into a new list, leaving the original
wordsuntouched.
You can also call a function you wrote yourself:
def label(n):
return f"Item #{n}"
labels = [label(n) for n in range(1, 4)]
print(labels) # ['Item #1', 'Item #2', 'Item #3']
range(1, 4)yields1, 2, 3.- For each,
label(n)builds a formatted string, and all three land in the new list.
Adding a filter with if
Often you want to keep only some items. Add an if condition at the end of the comprehension; only items for which the condition is True make it into the list:
numbers = range(10)
evens = [n for n in numbers if n % 2 == 0]
print(evens) # [0, 2, 4, 6, 8]
for n in numberswalks through0to9.if n % 2 == 0is the filter βn % 2is the remainder when dividing by 2, so== 0isTrueonly for even numbers.n(the expression) is added only when the filter passes.
You can transform and filter at the same time:
prices = [4, 12, 7, 20, 3]
big_with_tax = [round(p * 1.2, 2) for p in prices if p >= 10]
print(big_with_tax) # [14.4, 24.0]
- The filter
if p >= 10keeps only prices of 10 or more (12and20). - The expression
round(p * 1.2, 2)adds 20% tax and rounds to two decimal places. - Both steps happen in one clear line.
Filter versus choose-a-value
There are two ways to use if, and beginners often mix them up:
nums = [1, 2, 3, 4, 5]
# if at the END = filter (drops items)
odds = [n for n in nums if n % 2 == 1]
print(odds) # [1, 3, 5]
# if-else at the FRONT = choose a value (keeps every item)
labels = ["odd" if n % 2 == 1 else "even" for n in nums]
print(labels) # ['odd', 'even', 'odd', 'even', 'odd']
- The first uses
ifafter theforto remove items, so the result is shorter. - The second uses an
if/elsebefore theforto pick a value for every item, so the result is the same length as the input.
When NOT to use a comprehension
Comprehensions are for short, single-expression transforms. If the body needs several steps, prints things, or updates other variables, a normal loop is clearer:
# Harder to read crammed into a comprehension β keep this as a loop:
total = 0
for order in [12, 5, 8]:
print("Processing", order)
total += order
print("Total:", total) # Total: 25
Here we both print and accumulate a running total, so a plain loop communicates the intent better. A good rule: if you cannot read your comprehension aloud as one clear sentence, use a loop.
Worked example: cleaning a list of scores
A teacher has raw test scores as strings, some with stray spaces and some blank. We want a clean list of the passing scores (50 or above) as integers.
raw_scores = ["72", " 45 ", "", "88", "50", " 39"]
passing = [int(s) for s in raw_scores if s.strip().isdigit() and int(s) >= 50]
print(passing) # [72, 88, 50]
print("How many passed:", len(passing)) # 3
print("Average:", sum(passing) / len(passing)) # 70.0
How it works:
for s in raw_scoreswalks through each raw string.- The filter
s.strip().isdigit()first removes spaces, then checks the string is all digits β this safely skips the blank""and anything non-numeric. and int(s) >= 50keeps only scores of 50 or more.- The expression
int(s)converts each surviving string into a number for the final list.
From that clean list we can immediately count, sum and average. Doing the same with a manual loop would take three times the lines.
Try it yourself
Start with temps_c = [0, 15, 22, 37, -5, 100] (temperatures in Celsius):
- Build a comprehension that converts each to Fahrenheit with the expression
c * 9 / 5 + 32. - Build another that keeps only the temperatures above freezing (
if c > 0). - Build one that labels each as
"hot"or"cold"using an if-else at the front, splitting at 25 degrees.
Then take a list of words and build a comprehension of only the words longer than four letters, each uppercased. When your transforms grow complex, compare your comprehension to the equivalent loop in Python lists in depth and keep whichever reads more clearly.
Quick quiz
Test yourself and earn XP
What does [n * 2 for n in [1, 2, 3]] produce?
Each item n is doubled by the expression n * 2, building the new list [2, 4, 6].
Where does the filter go in a comprehension?
The optional if condition comes after the for clause: [x for x in items if condition].
What does [w.upper() for w in ["hi", "bye"]] produce?
The expression w.upper() transforms each word to uppercase, giving ['HI', 'BYE'].
What does [n for n in range(6) if n % 2 == 0] produce?
The filter keeps only numbers where n % 2 == 0 (the even ones): 0, 2 and 4.
When is a normal for-loop a better choice than a comprehension?
Comprehensions shine for short, single-expression transforms. If the logic is long or has side effects, a regular loop is clearer.
FAQ
Usually a little, yes. A list comprehension is optimised internally so it often runs slightly faster than the equivalent loop that calls .append() each time, because Python does less work managing the list. But the speed difference is small and rarely the reason to choose one. The real benefit is readability: a comprehension states 'build a list of THIS for each item' in a single line that a reader can take in at a glance. Choose a comprehension when it makes the intent clearer, not purely for speed.
Yes to both. You can nest loops: [(x, y) for x in range(2) for y in range(2)] builds every pair. And you can use a conditional EXPRESSION (not a filter) before the for to choose between two values: ["even" if n % 2 == 0 else "odd" for n in range(4)] gives ['even', 'odd', 'even', 'odd']. Note the difference: an if at the END filters items out, while an if-else at the FRONT picks a value for every item. Nested comprehensions are powerful but get hard to read quickly, so if one stops being obvious at a glance, rewrite it as a normal loop.
Keep exploring
More in Coding