Python List Comprehension: Syntax, if/else and Examples
A list comprehension builds a new list in one line: [expression for item in iterable if condition]. For example, [n * n for n in range(1, 6)] gives [1, 4, 9, 16, 25], and adding if n % 2 == 0 keeps only the even ones.
squares = [n * n for n in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
even_squares = [n * n for n in range(1, 6) if n % 2 == 0]
print(even_squares) # [4, 16]
That is the whole idea. The rest of this post shows the patterns you will actually use, including if/else, nested loops, dict and set comprehensions, generator expressions, and the cases where a plain loop is the better choice. Every block runs as-is in the browser Python terminal.
List comprehension syntax, read left to right
A comprehension is a compact version of a loop that appends to a list. These two produce the same result:
# the loop version
squares = []
for n in range(1, 6):
squares.append(n * n)
# the comprehension version
squares = [n * n for n in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
Read the comprehension as: "give me n * n, for each n in range(1, 6)". The expression comes first because it is what ends up in the list. If you are not yet comfortable with the loop version, start with the Lists and Loops lesson; comprehensions make far more sense once for and append are second nature.
Filtering with if
Put an if at the end to keep only the items that pass a test.
marks = [45, 82, 67, 91, 38, 74]
passed = [m for m in marks if m >= 50]
print(passed) # [82, 67, 91, 74]
names = [" asha ", "ROHIT", "meenakshi ", ""]
clean = [n.strip().title() for n in names if n.strip()]
print(clean) # ['Asha', 'Rohit', 'Meenakshi']
The second example transforms and filters in one step: blank entries are dropped, the rest are trimmed and title-cased. This kind of cleanup is the most common real-world use of a comprehension.
if/else inside a list comprehension
When you want to change every item rather than drop some, the condition moves to the front and needs an else. This is a conditional expression, not a filter.
marks = [45, 82, 67, 91, 38, 74]
results = ["pass" if m >= 50 else "fail" for m in marks]
print(results)
# ['fail', 'pass', 'pass', 'pass', 'fail', 'pass']
capped = [min(m, 80) for m in marks]
print(capped) # [45, 80, 67, 80, 38, 74]
The rule to remember:
ifat the end filters: fewer items come out.x if cond else yat the front transforms: the same number of items come out.
Writing [m for m in marks if m >= 50 else 0] is a SyntaxError, because a filter at the end cannot have an else. You can combine both forms, though: ["pass" if m >= 50 else "fail" for m in marks if m > 0].
Nested comprehensions: flatten a matrix
You can have more than one for. They run in the same order as nested loops written top to bottom, so the outer loop comes first.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
pairs = [(size, colour) for size in ("S", "M") for colour in ("red", "blue")]
print(pairs)
# [('S', 'red'), ('S', 'blue'), ('M', 'red'), ('M', 'blue')]
A comprehension inside another comprehension builds a list of lists. The classic example is transposing a matrix, a frequent warm-up question in coding rounds:
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(3)]
print(transposed) # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
grid = [[0] * 3 for _ in range(2)]
print(grid) # [[0, 0, 0], [0, 0, 0]]
That last line is the safe way to create a 2D grid. Writing [[0] * 3] * 2 instead gives you two references to the same inner list, so changing one row changes both.
Dict and set comprehensions
The same syntax works with curly braces. Use key: value for a dictionary and a single expression for a set.
scores = {"Asha": 92, "Rohit": 48, "Meenakshi": 75}
toppers = {name: s for name, s in scores.items() if s >= 75}
print(toppers) # {'Asha': 92, 'Meenakshi': 75}
lengths = {name: len(name) for name in scores}
print(lengths) # {'Asha': 4, 'Rohit': 5, 'Meenakshi': 9}
words = "the cat sat on the mat".split()
unique_lengths = {len(w) for w in words}
print(unique_lengths) # {2, 3}
Dictionaries keep insertion order, so the output order above is reliable. Sets do not guarantee any order, so never depend on how a set prints.
Generator expressions: the same syntax without the list
Swap the square brackets for round ones and you get a generator expression. It produces values one at a time instead of building the whole list in memory.
import sys
total = sum(n * n for n in range(1, 1001))
print(total) # 333833500
as_list = [n * n for n in range(10_000)]
as_gen = (n * n for n in range(10_000))
print(sys.getsizeof(as_list) > sys.getsizeof(as_gen)) # True
gen = (n for n in range(3))
print(list(gen)) # [0, 1, 2]
print(list(gen)) # []
When a generator is the only argument to a function, as in sum(...), you can drop the extra brackets. Use a generator when you only need to loop over the values once (sum, max, any, writing to a file). Use a list when you need to index it, check its length, or loop over it more than once, because a generator is used up after one pass, as the empty second list(gen) shows.
List comprehension vs map and filter
map and filter with a lambda do the same job, and you will see them in older code and in interview questions.
nums = [1, 2, 3, 4, 5, 6]
with_map = list(map(lambda n: n * 2, filter(lambda n: n % 2 == 0, nums)))
with_comp = [n * 2 for n in nums if n % 2 == 0]
print(with_map) # [4, 8, 12]
print(with_comp) # [4, 8, 12]
Most Python style guides prefer the comprehension here because it reads in one direction. map is still a good fit when you already have a named function, such as list(map(int, input().split())) for reading numbers. The Lambda, Map and Filter lesson covers both styles side by side.
Is a list comprehension faster than a for loop?
Sometimes slightly, not always, and rarely by enough to matter. You can measure it yourself with timeit:
import timeit
setup = "data = list(range(1000))"
loop = """
out = []
for n in data:
out.append(n * 2)
"""
comp = "[n * 2 for n in data]"
with_map = "list(map(lambda n: n * 2, data))"
for label, stmt in [("loop", loop), ("comprehension", comp), ("map+lambda", with_map)]:
seconds = timeit.timeit(stmt, setup, number=2000)
print(f"{label:<14}{seconds:.4f}s")
# prints three timings; the numbers depend on your machine
In repeated runs on my machine with Python 3.12, the comprehension and the append loop were close to each other, and map with a lambda was consistently the slowest of the three. Run it yourself before trusting any claim about speed, including this one. Choose a comprehension because it is clearer, not because you expect a big speed-up.
When NOT to use a list comprehension
A comprehension is for building a list. Avoid it in these cases.
For side effects. This works but builds a list of None values you never use:
names = ["Asha", "Rohit"]
[print(n) for n in names] # avoid: creates [None, None]
for n in names: # do this instead
print(n)
When the logic does not fit on one readable line. Two for clauses and one condition is about the limit. If you need several conditions, a try/except, or intermediate variables, write a normal loop or move the logic into a named function and call it: [parse_row(r) for r in rows].
When you need to stop early. A comprehension always runs to the end. If you are searching for the first match, use a loop with break, or next() with a generator: next((m for m in marks if m > 90), None).
One behaviour that often surprises people coming from Python 2: the loop variable inside a comprehension does not leak out.
x = "outer"
nums = [x for x in range(3)]
print(nums) # [0, 1, 2]
print(x) # outer
Practise it
Comprehensions stick only after you write a few dozen of them. Try these in the practice editor: extract all even numbers from a list, flatten a list of lists, build a {word: count} dictionary from a sentence, and find the first number above 90 using next(). Then work through the graded exercises in the List Comprehensions lesson, which run real Python in your browser with no install.