~25 min · comprehension, list-comp, dict-comp, generator-expression
Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
The single most Pythonic syntactic feature
If you watch experienced Python developers and ask "what's the one thing you write that beginners don't," the answer is comprehensions. They take loops that build collections — almost the entire genre of for-loop-then-append — and turn them into a single readable expression. Once you internalize them, you write fewer, shorter, more obviously-correct loops.
The four flavors
Square brackets give you a list comprehension. Curly braces with a colon give you a dict comprehension. Curly braces without a colon give you a set comprehension. Parentheses give you a generator expression — same syntax, but it produces values lazily instead of building a collection. The generator expression is what makes sum(x*x for x in nums) efficient, because no intermediate list is built.
The grammar — and why filter goes at the end
The grammar reads left-to-right but with a twist: the expression you want comes first, then the for, then the optional if filter. [x*2 for x in nums if x > 0]. Beginners often try to put the if first because that's how it would read in English. The Python order is the way it is so the value-producing expression is the most prominent thing.
Warning: Conditional expression vs filter — they're different. [x if x > 0 else 0 for x in nums] always produces an element (the if/else chooses what). [x for x in nums if x > 0] drops elements. Don't mix them up.
When to STOP using a comprehension
Comprehensions become unreadable past two for clauses or a deeply nested filter. If you're nesting comprehensions inside comprehensions and reaching for triple-nested loops in one line, that's the signal to write a normal for-loop. Pythonic isn't "cram it into one line." Pythonic is "match the syntax to the complexity."
Generator expressions — the hidden efficiency feature
A generator expression looks just like a list comprehension but with (...) instead of [...]. The difference: nothing is computed until you iterate. So sum(x*x for x in big_list) never builds an intermediate list — it computes one square at a time, adds, throws it away. For functions that consume an iterable (sum, any, all, max, min), pass a generator expression instead of a list comprehension. You can usually drop the parens too: sum(x*x for x in nums) is the same as sum((x*x for x in nums)).
Code
List comprehension — the for-loop-then-append killer·python
nums = [1, 2, 3, 4, 5]
# The verbose way
doubled = []
for n in nums:
doubled.append(n * 2)
print(doubled) # [2, 4, 6, 8, 10]
# The comprehension
doubled = [n * 2 for n in nums]
print(doubled) # [2, 4, 6, 8, 10]
# With a filter
evens_doubled = [n * 2 for n in nums if n % 2 == 0]
print(evens_doubled) # [4, 8]
Dict and set comprehensions·python
words = ["alpha", "beta", "gamma"]
# Dict comprehension — colon = dict
lengths = {w: len(w) for w in words}
print(lengths) # {'alpha': 5, 'beta': 4, 'gamma': 5}
# Set comprehension — no colon = set
length_set = {len(w) for w in words}
print(length_set) # {4, 5}
# Inverting a dict (when values are hashable)
original = {"a": 1, "b": 2, "c": 3}
flipped = {v: k for k, v in original.items()}
print(flipped) # {1: 'a', 2: 'b', 3: 'c'}
Generator expression — the hidden efficiency feature·python
nums = range(1_000_000)
# List comprehension — builds a million-element list, then sums it
total_list = sum([x * x for x in nums]) # uses ~30MB during compute
# Generator expression — computes one square at a time
total_gen = sum(x * x for x in nums) # uses ~constant memory
print(total_list == total_gen) # True — same answer
# When parens are already there, you can drop the inner ones
any(x > 100 for x in nums) # works
max(x * 2 for x in nums) # works
Conditional expression vs filter — careful·python
nums = [-2, -1, 0, 1, 2]
# FILTER — drops elements
filtered = [x for x in nums if x > 0]
print(filtered) # [1, 2]
# CONDITIONAL — keeps every element, transforms
transformed = [x if x > 0 else 0 for x in nums]
print(transformed) # [0, 0, 0, 1, 2]
# Both — filter AND transform
result = [x * 10 for x in nums if x > 0]
print(result) # [10, 20]
# 3x3 grid
grid = [[i*3 + j for j in range(3)] for i in range(3)]
print(grid) # [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
# Flatten — note the order: outer for first, inner for second
flat = [n for row in grid for n in row]
print(flat) # [0, 1, 2, 3, 4, 5, 6, 7, 8]
# Once a comprehension goes past two for clauses, just write a loop.
# Readability wins.
You're given prices = [("AAPL", 180), ("MSFT", 420), ("GOOG", 145), ("NVDA", 950), ("TSLA", 215)]. Use comprehensions only (no for-loops, no .append, no map/filter) to produce: (a) a list of just the tickers, (b) a dict {ticker: price}, (c) a list of tickers with price > 200, (d) a single number — the average price using sum(...) with a generator expression. Print all four.
Progress
Progress is local-only — sign in to sync across devices.