Overview
List comprehensions are one of Python's most distinctive features. They replace multi-line loops with a single expression and are usually faster than equivalent for loops. This tutorial covers syntax, conditions, nested comprehensions, and when not to use them.
Basic Syntax
[expression for item in iterable]
Example: square every number from 0 to 9.
# Loop version
squares = []
for n in range(10):
squares.append(n * n)
# Comprehension version
squares = [n * n for n in range(10)]
Both produce [0, 1, 4, 9, 16, 25, 36, 49, 64, 81].
Adding a Condition
# Only even numbers
evens = [n for n in range(20) if n % 2 == 0]
# Filter and transform
lengths = [len(word) for word in words if len(word) > 3]
If-Else Inside the Expression
labels = ["even" if n % 2 == 0 else "odd" for n in range(5)]
# ['even', 'odd', 'even', 'odd', 'even']
Note the difference: a trailing if filters items; an inline if/else before the for transforms values.
Nested Loops
pairs = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), ...]
matrix = [[1, 2], [3, 4], [5, 6]]
flattened = [n for row in matrix for n in row]
# [1, 2, 3, 4, 5, 6]
Comprehensions for Other Types
| Type | Syntax | Result |
|---|---|---|
| List | [x for x in it] | List |
| Set | {x for x in it} | Set (no duplicates) |
| Dictionary | {k: v for k, v in it} | Dictionary |
| Generator | (x for x in it) | Lazy iterator |
# Set comprehension
unique_lengths = {len(w) for w in words}
# Dictionary comprehension
word_lengths = {w: len(w) for w in words}
# Generator (memory efficient)
total = sum(n * n for n in range(1_000_000))
Performance Comparison
import timeit
loop_time = timeit.timeit(
"result = []\nfor n in range(1000): result.append(n*2)",
number=10000
)
comp_time = timeit.timeit(
"result = [n*2 for n in range(1000)]",
number=10000
)
print(f"Loop: {loop_time:.3f}s, Comprehension: {comp_time:.3f}s")
Comprehensions are typically 20–40% faster because the loop runs in optimized C code rather than Python bytecode.
When Not to Use Comprehensions
- Complex logic: if the expression needs more than two conditions, a regular loop is clearer.
- Side effects: comprehensions should produce values, not call
printor modify external state. - Very large datasets with chaining: use generators to avoid building intermediate lists.
- Debugging: breakpoints and stack traces are harder to follow inside a comprehension.
Common Mistakes
| Mistake | Correct form |
|---|---|
[x if x > 0 for x in nums] | [x for x in nums if x > 0] |
[x for x in nums if x > 0 else 0] | [x if x > 0 else 0 for x in nums] |
| Using a comprehension for side effects | Use a plain for loop |
