List comprehensions
List comprehensions are a Pythonic alternative to map() and filter() for common cases. Neither is wrong, it’s a matter of
style and readability. Most Python developers tend to prefer comprehensions for simple transformations, while map() and filter()
feel more natural in a strictly functional style.
[ <expression> for <item> in <iterable> if <condition> ]The if condition is optional, you only need it when filtering.
nums = [1, 2, 3, 4, 5, 6]
# map only — transform every elementsquares = [x ** 2 for x in nums]print(squares) # [1, 4, 9, 16, 25, 36]
# filter only — keep matching elementsevens = [x for x in nums if x % 2 == 0]print(evens) # [2, 4, 6]
# map + filter — transform and filter in one expressioneven_squares = [x ** 2 for x in nums if x % 2 == 0]print(even_squares) # [4, 16, 36]How it maps to map() and filter()
Section titled “How it maps to map() and filter()”A list comprehension is not a replacement for map() and filter(), it is a more readable way to express the same idea.
Under the hood, both approaches do the same work: filter elements, then transform them. The comprehension just collapses that into
a single, natural-reading line.
nums = [1, 2, 3, 4, 5, 6]
# These are equivalent: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 almost like plain English:# "give me x squared, for every x in nums, if x is even"Here is how both expressions map to the same two steps:
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]Beyond Lists — Dict and Set Comprehensions
Section titled “Beyond Lists — Dict and Set Comprehensions”The same idea extends to other collections. The syntax is almost identical, only the brackets and structure change to reflect
the collection type. Sets use curly braces and store unique values only, while dicts use curly braces with a key: value pair
to build a mapping.
nums = [1, 2, 3, 4, 5]
# Set comprehension — curly braces, no duplicatessquares_set = {x ** 2 for x in nums}print(squares_set) # {1, 4, 9, 16, 25}
# Dict comprehension — key: value pairssquares_dict = {x: x ** 2 for x in nums}print(squares_dict) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}Here is how they work:
Input: [1, 2, 3, 4, 5]. ← input list
═══════════════════════════════════════════════════════════════════ 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}The uniqueness of sets becomes relevant when your input contains duplicates, a set comprehension silently discards them, while a list comprehension keeps them all:
# input listnums = [1, 2, 2, 3, 3, 3]
print([x ** 2 for x in nums]) # [1, 4, 4, 9, 9, 9] ← duplicates keptprint({x ** 2 for x in nums}) # {1, 4, 9} ← duplicates droppedOne Important Difference from map() and filter()
Section titled “One Important Difference from map() and filter()”Final thoughs
Section titled “Final thoughs”Python offers list comprehensions as a more readable alternative for map and filter. Both are valid, the choice is largely
stylistic:
| Goal | map / filter | Comprehension |
|---|---|---|
| Transform | map(lambda x: x**2, nums) | [x**2 for x in nums] |
| Filter | filter(lambda x: x>3, nums) | [x for x in nums if x>3] |
| Both | map(..., filter(..., nums)) | [x**2 for x in nums if x>3] |
| Reduce | reduce(lambda a,x: a+x, nums) | No direct equivalent |
reduce() has no comprehension equivalent, it remains the go-to tool whenever you need to collapse a collection into
a single value.
Trio vs. comprehension
Section titled “Trio vs. comprehension”from functools import reduce
nums = [1, 2, 3, 4, 5]
# map(func, iterable) — transform every elementsquared = list(map(lambda x: x**2, nums))strings = list(map(str, nums)) # Built-in functions work tooprint(squared) # [1, 4, 9, 16, 25]print(strings) # ['1', '2', '3', '4', '5']
# map with multiple iterablesa = [1, 2, 3]b = [10, 20, 30]sums = list(map(lambda x, y: x + y, a, b))print(sums) # [11, 22, 33]
# filter(func, iterable) — keep elements where func returns Trueevens = list(filter(lambda x: x % 2 == 0, nums))positives = list(filter(None, [-1, 0, 2, None, 3, ""])) # None removes falsyprint(evens) # [2, 4]print(positives) # [-1, 2, 3]
# reduce(func, iterable) — fold iterable into single valuetotal = reduce(lambda acc, x: acc + x, nums) # 15product = reduce(lambda acc, x: acc * x, nums) # 120maximum = reduce(lambda a, b: a if a > b else b, nums) # 5
# With initial valuetotal = reduce(lambda acc, x: acc + x, nums, 100) # 115 (starts at 100)
print(total, product, maximum)# Python idiom — prefer comprehensions over map/filter for readability# These are equivalent:squared = list(map(lambda x: x**2, nums))squared = [x**2 for x in nums] # ← more Pythonic
evens = list(filter(lambda x: x % 2 == 0, nums))evens = [x for x in nums if x % 2 == 0] # ← more Pythonic
# But map/filter can be cleaner with existing named functionsstrings = list(map(str, nums)) # Clean — no lambda neededGotchas
Section titled “Gotchas”List comprehensions return a fully evaluated list immediately, no laziness, everything computed upfront and stored in memory.
# input listnums = [1, 2, 3, 4, 5]
lazy = map(lambda x: x ** 2, nums) # <map object> — not computed yeteager = [x ** 2 for x in nums] # [1, 4, 9, 16, 25] — computed immediately
print(type(lazy)) # <class 'map'>print(type(eager)) # <class 'list'> 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 ═══════════════════════════════════════════════════════════════So the key difference is:
map()andfilter(): lazy, compute on demand, memory efficient- List comprehension: eager, computes everything immediately, returns a plain
list
More examples
Section titled “More examples”# List comprehensionsquares = [x**2 for x in range(10)]evens = [x for x in range(20) if x % 2 == 0]matrix = [[i * j for j in range(1, 4)] for i in range(1, 4)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]print(evens) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]print(matrix) # [[1,2,3],[2,4,6],[3,6,9]]
# Dict comprehensionword_lengths = {word: len(word) for word in ["apple", "banana", "cherry"]}inverted = {v: k for k, v in {"a": 1, "b": 2}.items()}print(word_lengths) # {'apple': 5, 'banana': 6, 'cherry': 6}print(inverted) # {1: 'a', 2: 'b'}
# Set comprehensionunique_lengths = {len(word) for word in ["apple", "banana", "kiwi", "fig"]}print(unique_lengths) # {3, 4, 5, 6}
# Generator expression — lazy, memory-efficienttotal = sum(x**2 for x in range(1_000_000)) # No list created in memoryfirst_even = next(x for x in range(100) if x % 2 == 0 and x > 10)print(first_even) # 12
# Flatten nested list — functional stylenested = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]flat = [x for sublist in nested for x in sublist]print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]