By now you've written this loop a dozen times: make an empty list, loop over the data, maybe test each item, append a result. Python noticed how often everyone writes it — and gave it a dedicated one-line syntax. Once comprehensions click, you'll read and write half of real-world Python faster.
What you'll learn
What
A list comprehension builds a new list in a single expression: [expression for item in collection if condition]. It's the transform-and-filter loop, compressed — same behavior, same result, one line.
Why
This is the single most common idiom in professional Python. Data code is saturated with it: clean every value, extract one field per record, keep the rows that match. Reading other people's code — Stack Overflow answers, library sources, your teammates' pipelines — requires fluency in it.
Where it's used
Cleaning columns of values, extracting fields from records, filtering rows, building test fixtures, and everywhere a for-append loop used to live.
Where this runs in production
Ranking code routinely reshapes candidate lists with comprehensions — extracting IDs, filtering removed posts, mapping scores — before the heavy ranking model sees them.
Ingest scripts convert raw price strings to floats and drop unparseable ticks in a single comprehension per field — terse enough to audit at a glance.
Tokenization pipelines are full of comprehensions: [len(t) for t in tokens], [t for t in examples if t['text']] — the shape of virtually all preprocessing code.
prices = [19.99, 5.49, 12.00]
# The loop you know:
with_tax = []
for p in prices:
with_tax.append(round(p * 1.08, 2))
# The comprehension — identical result:
with_tax = [round(p * 1.08, 2) for p in prices]
print(with_tax)Four lines become one, and nothing else changed: same iteration, same transform, same new list. Every comprehension can be mechanically expanded back into its loop — when one confuses you, expand it.
A for-append loop is walking the kitchen line telling each cook individually what to do. A comprehension is the order ticket pinned above the pass: 'grilled [expression], for every table [iteration], skip the cancelled ones [filter]'. Same kitchen, same food — the ticket just states the whole job at once.
A TRAILING if filters — it decides which items get in: [x for x in xs if x > 0]. A LEADING if-else transforms — every item gets in, but conditionally shaped: [x if x > 0 else 0 for x in xs]. Filter selects; conditional expression reshapes. Mixing up the two positions is the #1 comprehension syntax error.
temps = [21.5, -3.0, 18.2, -1.5] warm_only = [t for t in temps if t > 0] # filter: 2 items clamped = [t if t > 0 else 0.0 for t in temps] # transform: 4 items print(warm_only) # [21.5, 18.2] print(clamped) # [21.5, 0.0, 18.2, 0.0]
Same data, both patterns. Filtering can change the length; transforming never does. Choosing between them is a data-design decision you'll make constantly in cleaning code (drop the bad rows, or impute them?).
Comprehensions optimize for readability — until they don't. Two nested fors, multiple conditions, or side effects (calling print/save inside one) tip it into write-only code. House rule used by most teams: if it doesn't fit comfortably on one line, or you're doing anything other than building a list, write the loop. A comprehension you have to expand mentally to understand has already failed.
Each element flows right-to-left through the clauses. Click each stage to see its job.
Convert a list of names to uppercase for a display board.
Read from the middle: for each n in names, produce n.upper(), collect the results. Three in, three out — a pure transform has no filter clause.
['ADA', 'GRACE', 'ALAN']
Your task
Clean a day of air-quality sensor readings using only comprehensions — no for-append loops. Build: (1) valid readings (drop the -999 error codes), (2) those same readings rounded to whole numbers, (3) an alert list where readings over 150 become 'HIGH' and the rest 'ok'.
Valid: [42.7, 155.2, 89.1, 170.8, 33.4] Rounded: [43, 155, 89, 171, 33] Alerts: ['ok', 'HIGH', 'ok', 'HIGH', 'ok']
Write your solution in the editor on the right, then hit Run.
What does [n * n for n in range(4)] produce?
Which comprehension keeps only the even numbers from nums, unchanged?
lengths = [len(w) for w in words if len(w) > 3] — with words = ['data', 'is', 'power']. What is lengths?
What's the code-review feedback?
Given usernames = [' Ada ', 'GRACE', ' alan'], build cleaned — each name stripped of whitespace and lowercased — using one comprehension. Print cleaned. Expected: ['ada', 'grace', 'alan']
Grades: scores = [88, 42, 95, 67, 55, 78]. In ONE comprehension build pass_fail where each score becomes 'pass' if 60 or above, else 'fail' — then print how many passed using .count(). Expected output: ['pass', 'fail', 'pass', 'pass', 'fail', 'pass'] 4
Which is the best reason to REJECT a comprehension in review and ask for a plain loop?
What is a list comprehension, and what are its three clauses?
A list comprehension is an expression that builds a new list from an existing iterable in one statement: [expression for item in iterable if condition]. The for clause is the engine — identical to a loop header, binding each element to a name. The expression says what each element becomes in the output — unchanged, computed, or conditionally shaped with a value-if-else. The optional trailing if filters which elements participate at all. The mental model I'd give: it's exactly the initialize-loop-test-append pattern compressed, and any comprehension can be mechanically expanded back into that four-line loop, which is also the right debugging move when one gets confusing.
Explain the difference between a trailing if and a leading if-else in a comprehension — including the effect on output length.
The trailing if — [x for x in xs if x > 0] — is a FILTER: it sits after the for, takes no else, and decides membership, so the output can be shorter than the input. The leading if-else — [x if x > 0 else 0 for x in xs] — is part of the EXPRESSION: it must have an else, every input element produces exactly one output element, and the length is always preserved. In data-cleaning terms, the trailing if drops bad rows while the leading if-else imputes or caps them — a genuine modeling decision disguised as syntax. The two also fail differently: writing a leading if without else is a SyntaxError, which is usually the first clue someone mixed up the positions.
When would you deliberately choose a plain for loop over a comprehension?
Three cases. First, side effects: if the body writes to a database, prints, or calls an API, a loop states that honestly — a comprehension implies you want the list it builds, and building a throwaway list to smuggle side effects is misleading. Second, complexity: multiple nested fors, several conditions, or logic that no longer fits comfortably on a line — if the reviewer has to mentally expand it, the compression bought nothing. Third, complex accumulation: when iterations depend on previous state (running totals with resets, early exit with break — which comprehensions don't support), the loop's explicit state is clearer. The heuristic I actually use: comprehension when the sentence is 'build a list of X from Y', loop when the sentence contains 'and then' or 'unless'.
Common Mistakes to Avoid
1) Leading if without else — [x if x > 0 for x in xs] is a SyntaxError; filters go at the END. 2) Building a list you never use just to run side effects — use a loop. 3) Nesting comprehensions until they're write-only — expand to a loop when it stops fitting on a line. 4) Filtering when you meant to transform (silently changing the list's length and misaligning it with a paired list). 5) Shadowing an existing variable with the loop name — for n in ns leaks nothing in Python 3, but reusing a meaningful outer name still confuses readers. 6) Reaching for a comprehension over huge numeric data where NumPy vectorization is the real tool.
Ask the AI Tutor
Try these prompts in the AI Tutor panel: • 'Give me five loops to convert into comprehensions, then check my answers.' • 'Show me a comprehension that's too clever and refactor it with me.' • 'Drill me on trailing-if vs leading-if-else with data cleaning examples.' • 'Preview how these comprehensions become pandas one-liners.' • 'Interview mode: ask me when NOT to use a comprehension and grade my answer.'
Glossary
List comprehension — an expression building a new list: [expr for item in iterable if cond]. Expression (in a comprehension) — what each element becomes in the output. Filter clause — the trailing if that decides membership. Conditional expression — value-if-else used as the expression; always needs else. Transform — producing one output element per input element (length preserved). sum() — built-in that totals an iterable. .count(x) — list method counting occurrences of x. Winsorizing — capping outliers at a threshold instead of dropping them. Dict/set comprehension — the same syntax with braces, building dicts or sets. Side effect — an action beyond computing a value (writing, printing).
Recommended Resources
• Docs: 'List Comprehensions' in the official Python tutorial — every variation with examples. • Read: PEP 202, the two-page proposal that added comprehensions — a rare readable language-design document. • Practice: grep any Python codebase you have for 'for' and count how many loops are secretly comprehensions waiting to happen; convert three. • Next in DSM: with control flow complete, you'll stop writing scripts and start building tools — Defining & Calling Functions opens the Functions module, where logic gets a name, inputs, and a returned result.
Recap
✓ [expression for item in collection if condition] — transform on the left, iterate in the middle, filter on the right. ✓ Read comprehensions from the for clause outward; expand to a loop when in doubt. ✓ Trailing if filters (length may shrink); leading if-else transforms (length preserved). ✓ Keep the list you build — side effects belong in loops. ✓ Density is the enemy: one line, one idea, or write the loop. ✓ Comprehensions train the apply-to-every-element thinking that NumPy and pandas run on. Next up: Defining & Calling Functions. Control Flow is complete — you can decide, repeat, steer, and build. Now you'll wrap that logic into named, reusable functions: the unit every pipeline, library, and test is made of.
Run your code to see the output here.