πŸ“
CodingπŸŽ“ Ages 14-18Intermediate 12 min read

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 it n (exactly like a normal for-loop).
  • n * n β€” the expression at the front: what to put in the new list for each n.
  • 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 expression w.upper() produces the uppercase version.
  • The results are collected into a new list, leaving the original words untouched.

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) yields 1, 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 numbers walks through 0 to 9.
  • if n % 2 == 0 is the filter β€” n % 2 is the remainder when dividing by 2, so == 0 is True only 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 >= 10 keeps only prices of 10 or more (12 and 20).
  • 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 if after the for to remove items, so the result is shorter.
  • The second uses an if/else before the for to 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:

  1. for s in raw_scores walks through each raw string.
  2. The filter s.strip().isdigit() first removes spaces, then checks the string is all digits β€” this safely skips the blank "" and anything non-numeric.
  3. and int(s) >= 50 keeps only scores of 50 or more.
  4. 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?

Where does the filter go in a comprehension?

What does [w.upper() for w in ["hi", "bye"]] produce?

What does [n for n in range(6) if n % 2 == 0] produce?

When is a normal for-loop a better choice than a comprehension?

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.