Question bank — List comprehensions — `[expr for x in iterable if condition]`
This page is a set of conceptual trip-wires for [[1.2.34 List comprehensions — [expr for x in iterable if condition] (Hinglish)|list comprehensions]]. No heavy computation lives here — every item targets a misconception or a boundary case. Read each prompt, decide your answer out loud, then reveal.
What the placeholders mean (read this first)
Before the traps, pin down the placeholder names used in the template [expr for x in iterable if condition]. Every formula on this page uses these exact stand-ins, so we define them once here and never switch names:
Throughout, remember the two anchors from the parent note:
- Execution order is
for→if→expr(pick the item, test it, then build it), even though we readexprfirst. - There are two different
ifs: the filterif(noelse, goes afterfor, drops items) and the conditionalif/else(a ternary, goes beforefor, chooses a value). The mnemonic: "Choose-before, Cut-after."
The figure below turns those two anchors into one picture — keep it in view while you work the traps:
![Figure — List comprehensions — `[expr for x in iterable if condition]`](/notes-assets/img/3c080d975a7ef609.webp)
Read the figure top-to-bottom. In the top line, the red arrow points at the ternary slot (a if t else b) sitting before the for — this is "choose-before", where t picks between values a and b. The black arrow points at the filter cond sitting after the for — "cut-after", where cond drops items. The lower chain shows what actually runs: the red for box fires first (pick x), then if (test), then expr (build) — the exact reverse of reading order. Every trap on this page is a stress-test of one of these two axes: which if and when does expr run.
The loop rewrites (all three shapes)
Not every comprehension has the same skeleton. Here are the loop translations for the three common shapes, so no case is left unexplained. (Recall it = iterable, cond = condition, and a/b/t are the ternary's two values and its test, all defined above.)
True or false — justify
Every list comprehension can be rewritten as an equivalent plain for loop with an append.
r=[]; for x in it: r.append(expr), and one with a filter and ternary becomes r=[]; for x in it: if cond: r.append(a if t else b). The translation always exists.The if condition filter part is mandatory.
expr, so the comprehension is a pure transform.In [x**2 for x in range(10) if x % 2 == 0], the x**2 is computed for all ten numbers.
expr. Only the five even numbers survive the if, so x**2 is computed exactly five times, not ten.A comprehension always returns a list, regardless of the brackets used.
[...] make a list. {...} builds a set or dict, and (...) builds a lazy generator, not a list.[n if n > 0 else n for n in nums] and [n for n in nums if n > 0] produce lists of the same length.
nums, keeping negatives unchanged here); the second filters, so it is usually shorter (drops the negatives entirely).In [cell for row in grid for cell in row], the rightmost for is the outermost loop.
for is the outermost loop. Multiple fors read top-to-bottom / outer-to-inner, exactly like nested for loops.A comprehension is always faster and clearer than the equivalent loop, so you should use it everywhere.
ifs, side effects, or 100-char lines destroy readability. If you can't read it aloud as one sentence, use a loop.The variable x in [x for x in range(3)] leaks into the surrounding code after the comprehension finishes.
for loop.[print(x) for x in range(3)] is a good way to print three numbers.
[None, None, None]. Use a plain for loop for side effects like printing.You can put two if filters after one for, and both must pass for the item to be kept.
[x for x in xs if x > 0 if x < 10] chains filters like and; an item survives only if every filter is True.In a multi-for comprehension, a filter can only test the outermost loop variable.
for to its left, so [x for a in A for x in a if x>0] filters the inner x, not a.Spot the error
[x for x in nums if x > 0 else 0] — what is wrong?
SyntaxError. A bare filter if (after for) can never take an else. If you want an else, you need a ternary before the for: [x if x > 0 else 0 for x in nums].[len(w) for w if len(w) > 3] — what is missing?
in iterable part of the loop. Every for x needs a source: it must read for w in words, otherwise Python has nowhere to draw items from.squares = [x**2 for x in range(5)]; print(x) — why might this surprise a Python-2 veteran?
x does not leak out of the comprehension, so print(x) raises NameError (unless x was defined earlier). Old Python-2 habits assumed the loop variable survived.result = [i + j for i in range(2)] for j in range(2)] — name both faults.
[...] closes after range(2), so the trailing for j ... )] is a SyntaxError. Second, even if fixed, for j belongs inside the brackets. The correct form merges both loops: [i + j for i in range(2) for j in range(2)].even_or_zero = [x if x % 2 == 0 for x in nums] — what is broken?
expr must be complete: it needs an else. Either add it (x if x % 2 == 0 else None) or, if you meant to filter, move it after the for as [x for x in nums if x % 2 == 0].[row for cell in row for row in grid] — why does this raise NameError?
fors are in the wrong order. The leftmost/first for must define the outer name; here cell in row is written before row exists. Correct order is for row in grid for cell in row.Why questions
Why does expr appear first in the written form when it actually runs last?
expr for each x" — the design favours reading intent (what you want) over execution mechanics (how it's built).Why does the ternary if/else go before the for but the filter if goes after?
expr (it must return a value for the output slot, which sits first), whereas the filter decides whether to loop-include an item, so it attaches to the for that follows.Why is a generator expression (x for x in it) sometimes preferred over a list comprehension?
Why can [n for n in nums] (no filter, expr = n) still be useful?
nums as a new list, so mutating the copy doesn't affect the original — sometimes clearer than list(nums) when combined with other logic.Why do map()/filter() and comprehensions overlap?
filter(); the comprehension just combines both in one readable expression instead of composing two function calls.Why does adding side effects (like .append to another list) inside a comprehension count as bad style?
Edge cases
What does [x for x in []] evaluate to?
[]. An empty iterable means the loop body never runs, so nothing is ever appended.What does [x for x in range(5) if False] produce?
[]. The filter is always False, so every item is dropped and the result is empty — a comprehension can legally build an empty list.What does [0 for _ in range(3)] build, and why the underscore?
[0, 0, 0]. The _ signals "I don't use the loop variable"; expr is a constant 0, so it just repeats the value once per iteration — a common way to pre-fill a list.Does [x for x in range(3) if x] include 0?
0 is falsy, so if x treats it as False and drops it. The result is [1, 2]. This is a classic trap: a bare if x filters out zero, empty strings, and None.What happens with [1/x for x in [2, 0, 4]]?
ZeroDivisionError at the middle item. Comprehensions don't skip errors — if expr fails for any surviving x, the whole comprehension aborts. Filter first: [1/x for x in [2,0,4] if x != 0].What does [x for x in "cat"] give, since a string is an iterable?
['c', 'a', 't']. Strings are iterables of characters, so for x in "cat" walks one character at a time — see range() and iterables.What does [k for k in {"a": 1, "b": 2}] produce, and why?
['a', 'b'] — iterating a dict yields its keys, not its values or pairs. To get values use .values(); to get both use .items(). This trips up many learners who expect the values.Recall One-line self-test
Cover the answers above. For each item, decide which of the two ifs is involved (filter vs ternary), and when expr runs relative to the filter. If you nail those two axes, every trap on this page collapses.
Connections
- [[1.2.34 List comprehensions — `[expr for x in iterable if condition]` (Hinglish)|Parent: List comprehensions]]
- For loops — the loop these traps unfold into.
- Conditional expressions (ternary) — the "choose-before"
if/else. - Dictionary and set comprehensions — same traps with
{}. - Generator expressions — the lazy
(...)cousin. - map() and filter() — functional twins of transform and filter.
- range() and iterables — what feeds the
forslot.