Skip to content

List comprehensions

A list comprehension builds a new list from any iterable in one expression. Instead of writing a for loop that appends to an empty list each iteration, you write the whole thing inline.

# Loop version
squares = []
for x in range(6):
squares.append(x ** 2)
# Comprehension — same result, one line
squares = [x ** 2 for x in range(6)]
# → [0, 1, 4, 9, 16, 25]

The general shape is:

Syntax
[<expression> for <item> in <iterable>]
[<expression> for <item> in <iterable> if <condition>]

The if condition is optional. Leave it out when you want every element.

Add a condition at the end to keep only elements that match:

nums = [1, 2, 3, 4, 5, 6]
evens = [x for x in nums if x % 2 == 0]
# → [2, 4, 6]
long_words = [w for w in ["hi", "hello", "hey", "howdy"] if len(w) > 3]
# → ["hello", "howdy"]

The loop equivalent makes the evaluation order clear: Python checks the condition first, then appends.

evens = []
for x in nums:
if x % 2 == 0: # check condition
evens.append(x)

Put the transform expression at the front and the if condition at the end to do both in one go:

nums = [1, 2, 3, 4, 5, 6]
even_squares = [x ** 2 for x in nums if x % 2 == 0]
# ^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^
# result source filter
# → [4, 16, 36]

Compared to map() + filter(), which does the same work but reads right to left:

# These produce the same result:
result = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, nums)))
result = [x**2 for x in nums if x % 2 == 0]
# The comprehension reads left to right: result, source, condition.
# map + filter reads inside out: filter first (inner), then map (outer).
Input: [1, 2, 3, 4, 5, 6]
═══════════════════════════════════════════════════════════════
map() + filter() List comprehension
═══════════════════════════════════════════════════════════════
filter(lambda x: x % 2 == 0) if x % 2 == 0
───────────────────────────── ─────────────
[ 1 ] ── False ── [ 1 ] ── False ──
[ 2 ] ── True ── [ 2 ] ──┐ [ 2 ] ── True ── [ 2 ] ──┐
[ 3 ] ── False ── [ 3 ] ── False ──
[ 4 ] ── True ── [ 4 ] ──┤ [ 4 ] ── True ── [ 4 ] ──┤
[ 5 ] ── False ── [ 5 ] ── False ──
[ 6 ] ── True ── [ 6 ] ──┤ [ 6 ] ── True ── [ 6 ] ──┤
map(lambda x: x**2) │ x ** 2 │
─────────────────── ──────────────── ─┤
[ 2 ] ── (x**2) ── [ 4 ] ──┐ [ 2 ] ── (x**2) ── [ 4 ] ──┐
[ 4 ] ── (x**2) ── [ 16 ] ──┤ [ 4 ] ── (x**2) ── [ 16 ] ──┤
[ 6 ] ── (x**2) ── [ 36 ] ──┤ [ 6 ] ── (x**2) ── [ 36 ] ──┤
[4, 16, 36] [4, 16, 36]

Same steps, same result. The comprehension is not faster; it just reads in a more natural order.

Two for clauses in one comprehension let you iterate over nested structure. The outer clause comes first, the inner one second:

nested = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
flat = [x for sublist in nested for x in sublist]
# ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
# outer: each sublist inner: each item in it
# → [1, 2, 3, 4, 5, 6, 7, 8, 9]

Reading it as two stacked for lines, top to bottom, shows what the comprehension expands to:

flat = []
for sublist in nested: # outer clause
for x in sublist: # inner clause
flat.append(x)

The same syntax works for sets and dicts. Only the brackets change:

nums = [1, 2, 3, 4, 5]
# List — ordered, allows duplicates
squares_list = [x ** 2 for x in nums]
# → [1, 4, 9, 16, 25]
# Set — unordered, no duplicates
squares_set = {x ** 2 for x in nums}
# → {1, 4, 9, 16, 25}
# Dict — key: value pairs
squares_dict = {x: x ** 2 for x in nums}
# → {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

The uniqueness of sets matters when your input has duplicates. A list comprehension keeps them; a set comprehension drops them:

nums = [1, 2, 2, 3, 3, 3]
print([x ** 2 for x in nums]) # [1, 4, 4, 9, 9, 9] ← duplicates kept
print({x ** 2 for x in nums}) # {1, 4, 9} ← duplicates dropped
Input: [1, 2, 3, 4, 5]
═════════════════════════════════════════════════════════════════════
List comprehension Set comprehension Dict comprehension
[x**2 for x in nums] {x**2 for x in nums} {x: x**2 for x in nums}
═════════════════════════════════════════════════════════════════════
[ 1 ] ── (x**2) ── 1 1 (unique) 1 ──► 1
[ 2 ] ── (x**2) ── 4 4 (unique) 2 ──► 4
[ 3 ] ── (x**2) ── 9 9 (unique) 3 ──► 9
[ 4 ] ── (x**2) ── 16 16 (unique) 4 ──► 16
[ 5 ] ── (x**2) ── 25 25 (unique) 5 ──► 25
[1, 4, 9, {1, 4, 9, {1:1, 2:4, 3:9,
16, 25] 16, 25} 4:16, 5:25}
ordered unordered, no duplicates key: value pairs
bracket [] curly braces {} curly braces {key: value}
Input: [1, 2, 3, 4, 5]
═══════════════════════════════════════════════════════════════
map() — lazy List comprehension — eager
═══════════════════════════════════════════════════════════════
map(lambda x: x**2, nums) [x**2 for x in nums]
returns immediately ──► <map object> returns immediately ──► [1, 4, 9, 16, 25]
nothing computed fully computed
values sit waiting all in memory
only computes
when consumed
next() ──► 1 (rest still waiting)
next() ──► 4 (rest still waiting)
next() ──► 9 (rest still waiting)
...
═══════════════════════════════════════════════════════════════
Memory cost one value at a time all N values at once
Best when large data, early exit small data, need all values
═══════════════════════════════════════════════════════════════

Both approaches do the same work. The choice is mostly stylistic:

Goalmap / filterComprehension
Transformmap(lambda x: x**2, nums)[x**2 for x in nums]
Filterfilter(lambda x: x>3, nums)[x for x in nums if x>3]
Bothmap(..., filter(..., nums))[x**2 for x in nums if x>3]
Reducereduce(lambda a,x: a+x, nums)No equivalent

map() can be shorter when you already have a named function, since you can pass it without a lambda:

words = ["hello", "world"]
list(map(str.upper, words)) # ["HELLO", "WORLD"] — no lambda needed
[w.upper() for w in words] # same result, method call style

reduce() has no comprehension equivalent. It stays the right tool whenever you need to collapse a collection into a single value.