When I switched from Java to Python, list comprehensions looked like showing off. Why cram a loop into one line? Isn't that just harder to read?

Then I got used to them. Now regular loops feel clunky for simple transformations. List comprehensions aren't about being clever—they're about expressing "transform this list into that list" without all the ceremony.

The Basic Pattern

Traditional approach:

numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
    squares.append(n ** 2)

List comprehension:

squares = [n ** 2 for n in numbers]

Same result, one line instead of four. Reads almost like English: "n squared for each n in numbers."

The pattern is: [expression for item in iterable]

Filtering

Add a condition at the end to filter:

# Only even numbers
evens = [x for x in range(20) if x % 2 == 0]

# Only strings that aren't empty
non_empty = [s for s in strings if s]

# Only users who are active
active = [u for u in users if u.is_active]

This replaces filter() in most cases, and I find it more readable.

if...else (Ternary)

Filtering keeps or discards items. But what if you want to transform differently based on a condition?

# Replace negatives with zero
clamped = [x if x > 0 else 0 for x in numbers]

# Uppercase vowels, lowercase consonants
processed = [c.upper() if c in 'aeiou' else c.lower() for c in text]

Note the syntax change: the condition moves to the front, before for. It's the ternary expression value_if_true if condition else value_if_false inside the comprehension.

If this gets confusing, just use a regular loop. Readability beats cleverness.

Nested Loops

You can nest loops. This flattens a matrix:

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

flat = [num for row in matrix for num in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Read it left to right: "give me num, for each row in matrix, for each num in that row."

The order matches how you'd write nested for loops:

flat = []
for row in matrix:
    for num in row:
        flat.append(num)

I usually stop at two levels. Triple-nested comprehensions are too hard to read.

Performance

List comprehensions are faster than equivalent for-loops with .append(). Why? The loop happens in C, inside the interpreter. No Python method lookup on every iteration.

For simple operations, comprehensions can be 10-50% faster. Not usually worth optimizing for, but nice to know.

Memory: Generators

List comprehensions build the entire list in memory immediately. Processing 100 million records? That's 100 million items in RAM.

Swap brackets for parentheses to get a generator:

# List - all in memory
squares = [x**2 for x in range(10000000)]  # ~800MB

# Generator - computed on demand
squares = (x**2 for x in range(10000000))  # ~0MB

Generators compute values lazily. You can iterate over them, but only once, and you can't index into them.

# Processing large files
lines = (line.strip() for line in open('huge.txt'))
for line in lines:
    process(line)
# Only one line in memory at a time

When Not to Use Them

My rule: if I have to think for more than 5 seconds to understand what a comprehension does, it should be a loop.

# ❌ Too clever
result = [x.process().validate() if x.is_valid() and x.type in valid_types 
          else x.default() for x in items if x is not None]

# ✅ Just use a loop
result = []
for x in items:
    if x is None:
        continue
    if x.is_valid() and x.type in valid_types:
        result.append(x.process().validate())
    else:
        result.append(x.default())

Also, don't use comprehensions for side effects. This is abuse:

# ❌ Don't do this
[print(x) for x in items]  # Creates a useless list of Nones

# ✅ Just loop
for x in items:
    print(x)

Dict and Set Comprehensions

Same idea, different brackets:

# Dict comprehension
word_lengths = {word: len(word) for word in words}

# Set comprehension
unique_lengths = {len(word) for word in words}

When to Reach for Comprehensions

Use them for simple transformations:

Use regular loops for complex logic, multiple statements per iteration, or when you need break/continue.

Once you get comfortable with the syntax, comprehensions become second nature. They're not about writing less code—they're about expressing data transformations more directly.

← Back to Python Articles

Back to Home