AlgoMaster Logo

List Comprehensions

High Priority19 min readUpdated June 6, 2026
Listen to this chapter
Unlock Audio

A list comprehension is Python's shorthand for the very common pattern of "start with an empty list, loop over an iterable, transform each item (and maybe filter), append the result". Instead of writing four lines of code, you write one expression that says what you want. This lesson covers the basic and filtered forms, when a comprehension is the right call, and when reaching for one would just make your code harder to read.

From for + append to a Single Expression

Most loops over a list end the same way: build a new list from the items. You start with an empty list, walk the original, do something to each item, and append the result. That pattern is so common that Python gives you a one-line form for it.

Here's the long version first. A product catalog stores names in mixed case, and you want them in uppercase for a heading row in a receipt.

Three lines of loop scaffolding for a single transformation. The list comprehension says the same thing in one line:

Read it left to right: "give me name.upper() for each name in products". The result is a brand new list. The original products is untouched.

The shape is always the same: square brackets around an expression, then a for ... in ... clause that names each item.

The expression on the left is what ends up in each slot of the new list. The for clause on the right binds item to each value of the iterable, one at a time, exactly like a regular for loop. Whatever the expression evaluates to gets appended to the result.

Doubling every price for a 2x promo is one expression now:

Lowercasing customer emails before saving them, same pattern:

Or extracting just the names from a list of (name, price) tuples:

The for clause supports the same tuple unpacking that regular for loops do. The expression on the left can use any of the unpacked names.

Adding a Filter

The basic form transforms every item. Often you want to skip some items too. That's the filtered form: add an if clause at the end of the comprehension, and only items where the condition is true make it into the result.

Reading order is still left to right. The for clause produces each item, the if clause decides whether to keep it, and the expression on the left transforms what gets kept.

A common case: pull out only the products that are in stock from a list of (name, count) pairs.

The for clause unpacks each pair into name and count. The if clause keeps only the pairs where count > 0. The expression on the left throws away count and keeps just name.

Filtering prices above a discount threshold works the same way:

You can transform and filter in the same comprehension. Here's "double the price of every item that's currently under $20", returning only the doubled values:

Three prices passed the filter (4.99, 14.99, 9.99), and each got doubled before going into the result. The order of clauses matters: for first, then if, then the expression on the left consumes whatever survived.

if/else Inside the Expression Is a Different Beast

There's a second place you can put if in a comprehension, and it does something completely different. If you write if/else to the left of for, you're using a conditional expression to choose what each item becomes. If you write if (no else) to the right of for, you're filtering.

Filter form (right of for, no else): "keep items where..."

The negatives are dropped. The result has three items.

Transform form (left of for, must have else): "for each item, give back either A or B"

Nothing is dropped. Every item ends up in the result, but the negatives are replaced with 0. The price if price > 0 else 0 part is just Python's conditional expression (the ternary), used as the expression slot of the comprehension.

The rule of thumb: if you want to skip items, put if after the for clause. If you want to replace items based on a condition, put if/else in the expression slot before the for. Mixing them up is one of the most common comprehension bugs.

You can even combine both: replace negatives with zero, but also skip items larger than 100.

Two ifs, two different jobs. The right-hand if price <= 100 filters out 200. The left-hand price if price > 0 else 0 rewrites the survivors.

A Side-by-Side Comparison

To make the difference between the loop form and the comprehension form concrete, here are the four most common everyday transformations written both ways.

Uppercase every product title:

Double every price for a 2x promo:

Lowercase customer emails:

Keep only in-stock products:

The pattern is the same every time. Anything you can write as "empty list, loop, optional filter, append a transformed value" can become a single comprehension.

The pipeline is always the same shape. Items flow from the source iterable into an optional filter, the survivors get transformed by the expression on the left, and the results land in a new list. Every list comprehension you'll ever write fits this picture.

When NOT to Use a Comprehension

Comprehensions are tempting once you learn them, and the temptation is to use them everywhere. Resist that. They're the right tool for simple transformations. They're the wrong tool for everything else.

Don't use a comprehension just to do side effects. A side effect is an action that doesn't produce a value you care about, like printing to the screen, logging, or writing to a file. The comprehension still builds a list of all the return values (mostly Nones), then throws it away. The code looks compact but does pointless work and confuses readers.

What's wrong with this code?

It does print the names, but it also builds a throwaway list [None, None, None] and immediately discards it. The reader has to stop and ask "wait, why is there a list here?". Use a regular for loop when you only want side effects:

The intent is clearer and there's no wasted allocation.

Don't use a comprehension for complex multi-step logic. Once the expression slot needs branching, intermediate variables, or two function calls layered together, the comprehension stops reading well. Compare:

The comprehension is correct, but you have to parse three conditions and a conditional expression on one line. A plain loop with named intermediate steps reads better:

Same result, easier to read, easier to debug. The rule isn't "shorter is better"; it's "clearer is better". A good signal: if the comprehension doesn't fit on one line, or if you'd have to read it twice to be sure what it does, write it as a loop.

A few more cases where a regular loop wins:

  • You need a try/except around each step.
  • You're updating something outside the comprehension (a counter, a running total) on each iteration.
  • The transformation needs more than one statement.
  • Two reviewers on your team can't agree on what the comprehension does.

Comprehensions Allocate a New List

A list comprehension always builds a full list in memory. That's fine for a few thousand items; it gets expensive when the source is huge or when you only need to walk the result once.

If you have a million products and you only need the sum of their doubled prices, building the full doubled list is wasteful. Python has a sibling syntax called a generator expression that uses parentheses instead of square brackets:

The (price * 2 for price in prices) part is a generator expression. It doesn't build a list; it yields one value at a time as sum() asks for it. For small inputs the difference is invisible. For large inputs it can be the difference between using a few bytes and a few gigabytes of memory.

Don't worry about generators yet. The point here is just to know they exist and that comprehensions are not the only option for transforming iterables.

What About Other Comprehensions?

Python also has dict comprehensions and set comprehensions, with the same shape but different brackets:

The list comprehension is the workhorse you'll reach for most often.

Quiz

List Comprehensions Quiz

10 quizzes