List & Dict Comprehensions
Remember building lists with loops — empty list, append inside, three lines? Comprehensions do it in one line, and they’re the single most “Pythonic” skill in the language. Code that uses them reads like Python was meant to read.
The upgrade path
The loop you know:
squares = []
for n in range(1, 6):
squares.append(n ** 2)
print(squares) # [1, 4, 9, 16, 25]
The comprehension — same result, one line:
squares = [n ** 2 for n in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
Read the comprehension left to right as a sentence: “give me n squared, for each n in range 1 to 6.” The expression comes first, the loop after — backwards from a for loop, but natural once you read it as the outcome you’re collecting.
Adding a condition: the filter
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
evens = [n for n in numbers if n % 2 == 0]
print(evens) # [2, 4, 6, 8]
big_evens = [n for n in numbers if n % 2 == 0 and n > 4]
print(big_evens) # [6, 8]
An if at the end keeps only matching items. Full sentence: “give me n, for each n in numbers, if n is even.”
Transform AND filter together
words = ["apple", "banana", "avocado", "cherry", "apricot"]
a_words = [w.upper() for w in words if w.startswith("a")]
print(a_words) # ['APPLE', 'AVOCADO', 'APRICOT']
Expression (transform) + loop (source) + condition (filter) — the three-part anatomy of every comprehension.
Dictionary comprehensions
Same idea, building dicts — {key: value for ...}:
marks = {"Aarav": 92, "Diya": 95, "Kabir": 78}
# invert a dict
inverted = {v: k for k, v in marks.items()}
print(inverted) # {92: 'Aarav', 95: 'Diya', 78: 'Kabir'}
# filter + transform
toppers = {name: mark for name, mark in marks.items() if mark > 80}
print(toppers) # {'Aarav': 92, 'Diya': 95}
# word lengths
words = ["apple", "banana", "kiwi"]
lengths = {w: len(w) for w in words}
print(lengths) # {'apple': 5, 'banana': 6, 'kiwi': 4}
That last one — building a dict from a list — is a comprehension you’ll write weekly.
Real-world one-liners you’ll actually use
# clean a messy list of strings
raw = [" Aarav ", "DIYA", " kabir "]
names = [name.strip().title() for name in raw]
print(names) # ['Aarav', 'Diya', 'Kabir']
# numbers from strings (the input-conversion pattern!)
raw_input = ["10", "20", "30"]
values = [int(x) for x in raw_input]
# flatten a list of lists
grid = [[1, 2], [3, 4], [5, 6]]
flat = [num for row in grid for num in row]
print(flat) # [1, 2, 3, 4, 5, 6]
That last one has TWO for clauses — “for each row in grid, for each num in row.” Nested loops in one line. Readable? Debatable. Useful? Extremely.
When NOT to comprehend
If the logic needs multiple steps, try/except, or more than one condition plus a transform — use a regular loop. A comprehension you can’t read in one breath is worse than a loop:
# ❌ too clever — nobody wants to parse this
result = [process(x) for x in data if x.valid and x.size > 10 and not x.skip and check(x)]
# ✅ a loop is honest about complexity
result = []
for x in data:
if x.valid and x.size > 10 and not x.skip and check(x):
result.append(process(x))
Comprehensions are for one clear transformation, not for hiding complexity.
Common Errors & Fixes
NameErroron the loop variable outside — the variable leaks after the comprehension in some Python versions; don’t rely on it existing.SyntaxErrorwith if/else placement — filter-if goes at the END ([x for x in xs if cond]); transform-if-else goes at the FRONT ([x if cond else y for x in xs]). Different positions, different jobs!- Missing brackets — comprehensions live inside
[]or{}; a bare comprehension is a syntax error.
✅ Checkpoint
- Comprehension for cubes of 1–5? ([n ** 3 for n in range(1, 6)])
- Keep only strings longer than 5 from
words? ([w for w in words if len(w) > 5]) - Build
{word: len(word)}from a list? ({w: len(w) for w in words}) - Where does the filter-if go vs the if-else transform? (Filter: end. If-else: front.)
Next: generators — producing values lazily and handling infinite sequences.