Python List Comprehensions: The Complete Guide for 2026
List comprehensions are one of Python’s most loved features: a compact way to build lists by transforming and filtering data. They can replace many small loops, make intent obvious, and often run faster than equivalent for loops.
In this guide you will learn the syntax, common patterns, and when not to use comprehensions. We’ll also cover dict/set comprehensions and generator expressions so you can choose the best tool for the job.
Table of Contents
1. Syntax and mental model
The core structure is:
[expression for item in iterable]
Optionally add a filter:
[expression for item in iterable if condition]
You can translate a comprehension into a loop like this:
| List comprehension | Equivalent loop |
|---|---|
[f(x) for x in xs] |
|
2. Transforming items
Any expression is allowed on the left side: function calls, attribute access, arithmetic, string formatting.
# Uppercase all names
names = [name.upper() for name in ['alice', 'bob']]
# Extract a field from dicts
ids = [row['id'] for row in rows]
# Compute derived values
areas = [r * r * 3.14159 for r in radii]
3. Filtering with if
The trailing if acts as a filter. Items that do not match are omitted entirely.
# Keep only even numbers
evens = [x for x in nums if x % 2 == 0]
# Keep only non-empty strings
non_empty = [s for s in strings if s]
# Keep only files with a certain extension
py_files = [p for p in paths if p.endswith('.py')]
4. Inline if/else vs filter
There are two different “ifs” you can use:
- Filter:
[x for x in xs if cond](removes items) - Conditional expression:
[a if cond else b for x in xs](keeps items, transforms differently)
# Filter: keep only positive numbers
positives = [x for x in nums if x > 0]
# Conditional expression: keep all numbers, but replace negatives with 0
clamped = [x if x > 0 else 0 for x in nums]
if in the wrong place. The else belongs to the expression, not the filter.
5. Nested comprehensions
You can nest loops in a comprehension. The order matches normal Python loop nesting.
# Flatten a 2D list
flat = [x for row in matrix for x in row]
# Create pairs (cartesian product)
pairs = [(x, y) for x in xs for y in ys]
# Nested with filter
coords = [(r, c) for r in range(rows) for c in range(cols) if grid[r][c] == '#']
6. Dict and set comprehensions
The same idea works for dicts and sets:
# Dict comprehension
by_id = {row['id']: row for row in rows}
# Set comprehension (unique values)
domains = {email.split('@')[1] for email in emails}
7. Generator expressions
A generator expression looks like a list comprehension but uses parentheses. It produces items lazily.
# List (eager)
squares_list = [x*x for x in range(10_000_000)]
# Generator (lazy)
squares_gen = (x*x for x in range(10_000_000))
# Consume lazily
total = sum(x*x for x in range(1_000_000))
8. Readability and performance
Some practical guidelines:
- If it reads like a sentence, a comprehension is great.
- If you need multiple statements, use a loop.
- If you are doing side effects, do not use a comprehension.
- If the input is huge and you only need streaming, use a generator expression.
| Goal | Best tool |
|---|---|
| Build a small list | List comprehension |
| Build a dict by key | Dict comprehension |
| Unique values | Set comprehension |
| Stream values into sum/any/all | Generator expression |
| Complex logic / multiple statements | For loop |
9. Real-world examples
Parse and normalize data
# Trim whitespace and drop empties
clean = [s.strip() for s in lines if s.strip()]
Extract nested fields from JSON-like data
# Keep only users with an email
emails = [u['email'].lower() for u in users if u.get('email')]
Convert types safely
def to_int(s):
try:
return int(s)
except ValueError:
return None
ints = [x for x in (to_int(s) for s in strings) if x is not None]
FAQ
Why does my list comprehension return a list of None?
This usually happens when you call a function that returns None (like list.sort()) or you use a comprehension for side effects. Use a loop for side effects, or return a value from your function.
Can I use async/await in a comprehension?
You can use comprehensions inside async functions, but you cannot await directly inside the expression in older Python versions. Prefer explicit loops or gather patterns when doing async I/O.