🐍

Python List Comprehensions: Syntax, Examples, and Pitfalls

Python4 min readUpdated October 2026

Learn how Python list comprehensions work, with filtering, nested loops, dict and set versions, and clear rules for when a plain loop is the better choice.

A list comprehension is a short way to build a new list from an existing one. Once it clicks, you'll use it every day. Before it clicks, it looks like a puzzle.

Here's the plan: start from the loop you already know, turn it into a comprehension step by step, and then look at when you should not use one.

From loop to comprehension

Say you want the squares of the numbers 1 through 5. With a regular loop:

squares = []
for n in range(1, 6):
    squares.append(n * n)

print(squares)
[1, 4, 9, 16, 25]

The comprehension version does the same job in one line:

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

Read it from left to right: "give me n * n for each n in the range." The pattern is always:

[expression for item in iterable]

The expression is what goes into the new list. The for part says where the items come from.

Adding a filter

Put an if at the end to keep only some items.

numbers = [3, 8, 11, 14, 21, 26]
evens = [n for n in numbers if n % 2 == 0]
print(evens)
[8, 14, 26]

The condition decides which items get in. Items that fail it are skipped, not replaced.

Changing values with if/else

This trips people up. When you want to transform every item differently depending on a condition, the if/else goes at the front, because it's part of the expression:

scores = [92, 48, 75, 61, 30]
results = ["pass" if s >= 60 else "fail" for s in scores]
print(results)
['pass', 'fail', 'pass', 'pass', 'fail']

A quick memory aid: a lone if goes at the end and filters. An if/else goes at the front and transforms.

Working with text and real data

Comprehensions are great for cleaning up messy input.

raw = ["  Alice ", "BOB", "  carol", ""]
names = [name.strip().title() for name in raw if name.strip()]
print(names)
['Alice', 'Bob', 'Carol']

You can also pull one field out of a list of dictionaries:

users = [
    {"name": "Ana", "age": 31},
    {"name": "Ben", "age": 17},
    {"name": "Cy", "age": 45},
]
adult_names = [u["name"] for u in users if u["age"] >= 18]
print(adult_names)
['Ana', 'Cy']

Nested loops

You can use more than one for. They read in the same order as nested loops would.

pairs = [(color, size) for color in ["red", "blue"] for size in ["S", "M"]]
print(pairs)
[('red', 'S'), ('red', 'M'), ('blue', 'S'), ('blue', 'M')]

To flatten a list of lists, it looks like this:

grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [value for row in grid for value in row]
print(flat)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

The outer loop comes first (for row in grid) and the inner loop second. If you ever find that confusing, that's a hint to use a regular loop instead.

Dictionary and set comprehensions

The same idea works with curly braces.

words = ["apple", "kiwi", "banana"]

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

first_letters = {w[0] for w in words}
print(sorted(first_letters))
{'apple': 5, 'kiwi': 4, 'banana': 6}
['a', 'b', 'k']

A dictionary comprehension uses key: value. A set comprehension has a single value and removes duplicates automatically.

When not to use a comprehension

Short and readable is the whole point. Skip the comprehension when:

  • It needs more than one condition and one loop. If you have to squint, split it up.
  • You're calling a function just for its side effect. Use a for loop when you want to print or save things. A comprehension that builds a throwaway list is wasteful and confusing.
  • You don't need the whole list. If you're only going to loop over the results once, or pass them to sum() or any(), use a generator expression with round brackets. It doesn't build the list in memory.
big_total = sum(n * n for n in range(1_000_000))
print(big_total)
333332833333500000

Compare these two. Which would you rather read six months from now?

# Hard to read
result = [y for x in data if x.active for y in x.items if y.price > 10 and y.stock]

# Easier
result = []
for x in data:
    if not x.active:
        continue
    for y in x.items:
        if y.price > 10 and y.stock:
            result.append(y)

The loop is longer, but anyone can follow it. Code gets read far more often than it gets written.

Common mistakes

  • Modifying the list while looping over it. Build a new list instead. That's exactly what a comprehension does.
  • Forgetting that variables stay inside. In Python 3, the loop variable in a comprehension doesn't leak out into the rest of your code.
  • Expecting None results. [print(n) for n in range(3)] prints the numbers, but it also builds a list of None values you didn't ask for.

Key takeaways

  • The pattern is [expression for item in iterable if condition].
  • A trailing if filters. A leading if/else transforms.
  • Dict and set comprehensions use the same idea with {}.
  • If a comprehension is hard to read, write a loop. Nobody gives out points for fitting it on one line.

Try it yourself

Reading helps, but typing the code is what makes it stick. Open the editor, paste any example from this article, and change it until it breaks.

Open the Python editor

Related Articles