Skip to main content

Python List Comprehensions

List Satellite

List Comprehensions

Transform loops into concise expressions without sacrificing readability.

Syntax

A list comprehension builds a new list by applying an expression to each item of an iterable:

[expression for item in iterable if condition]

The loop version and the comprehension version produce the same list:

names = ['ada', 'grace', 'linus']

# loop version
capitalized = []
for name in names:
capitalized.append(name.title())

# comprehension version
capitalized = [name.title() for name in names]

print(capitalized) # -> ['Ada', 'Grace', 'Linus']

Any expression works on the left side — arithmetic, method calls, function calls:

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

Filtering with if

A trailing if keeps only the items that pass the test. Items that fail are skipped entirely, so the result can be shorter than the input:

scores = [82, 45, 91, 60, 38, 77]
passing = [s for s in scores if s >= 60]
print(passing) # -> [82, 91, 60, 77]

Filters combine naturally with a transforming expression:

even_squares = [n * n for n in range(10) if n % 2 == 0]
print(even_squares) # -> [0, 4, 16, 36, 64]

if/else inside the expression

There is a second place an if can appear: before the for, as a conditional expression. This form transforms every item rather than filtering — the output has the same length as the input:

scores = [82, 45, 91, 60, 38, 77]
labels = ['pass' if s >= 60 else 'fail' for s in scores]
print(labels) # -> ['pass', 'fail', 'pass', 'pass', 'fail', 'pass']

The two forms answer different questions:

  • [s for s in scores if s >= 60]which items to keep (no else allowed).
  • ['pass' if s >= 60 else 'fail' for s in scores]what value to produce for each item (else required).

You can combine them: [s * 2 if s < 50 else s for s in scores if s > 0] first filters, then transforms.

Nested comprehensions

Two for clauses iterate like nested loops, left to right. The classic use is flattening a matrix:

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]

Read the clauses in the same order you would write the loop: for row in grid first, then for value in row.

To keep the nested structure and transform each element, nest one comprehension inside another instead:

doubled_grid = [[value * 2 for value in row] for row in grid]
print(doubled_grid) # -> [[2, 4, 6], [8, 10, 12], [14, 16, 18]]

Dict and set comprehensions

The same syntax builds dictionaries with {key: value ...} and sets with {expression ...}:

words = ['apple', 'banana', 'cherry']

lengths = {w: len(w) for w in words}
print(lengths) # -> {'apple': 5, 'banana': 6, 'cherry': 6}

unique_lengths = {len(w) for w in words}
print(unique_lengths) # -> {5, 6} — duplicates collapse automatically

A generator expression uses parentheses and produces items lazily — ideal when you only need to iterate once, such as feeding sum, any, or all:

total = sum(n * n for n in range(6))
print(total) # -> 55

When a plain loop is clearer

Comprehensions shine for a single transform-and-filter step. Reach for a regular loop when:

  • The body has side effects — printing, writing files, appending to several lists.
  • You need multiple statements — intermediate variables, error handling, logging.
  • Nesting gets deep — two for clauses is the practical limit; three is a puzzle.
totals = []
for order in [120, 40, 75]:
if order > 50:
discounted = order * 0.9
totals.append(round(discounted, 2))
print(f'discount applied to {order}')
# discount applied to 120
# discount applied to 75

print(totals) # -> [108.0, 67.5]

Squeezing that into one comprehension would mean dropping the print and recomputing the discount inline — the loop says it more plainly.

Frequently Asked Questions

Are list comprehensions faster than for loops?

Usually modestly faster, because the append happens in optimized bytecode instead of a repeated method lookup and call. The difference rarely matters compared to the work done per item, so choose the form that reads best and only micro-optimize with measurements.

Can a list comprehension have an else?

Only in the conditional expression before the for, which must have both branches: [x if cond else y for ...]. The trailing if is a filter and cannot take an else — writing [x for x in items if cond else y] is a syntax error.

labels = ['big' if n > 10 else 'small' for n in [4, 40]]
print(labels) # -> ['small', 'big']

How do I flatten a list of lists?

Use two for clauses in one comprehension, written in the same order as the equivalent nested loop. For deeply or irregularly nested data, itertools.chain.from_iterable or a recursive helper is clearer.

grid = [[1, 2], [3, 4]]
flat = [v for row in grid for v in row]
print(flat) # -> [1, 2, 3, 4]

Next up in your learning path