Python · Intermediate

Comprehensions in Python

8 min readUpdated September 24, 2026Every example verified

In short: A comprehension builds a list, dictionary or set from an iterable in a single expression: [expr for item in iterable if condition]. It replaces the common loop that starts with an empty collection and appends inside the body, and it states what the result is rather than how to build it.

What a comprehension is and why it reads better

Many loops have the same shape: start with an empty list, walk over some items, maybe skip a few, transform each one and append the result. A comprehension writes that whole pattern as one expression that reads like a description of the result: [w for w in weights if w > 10] is "the weights that are over ten". The intent is visible at a glance, and there is no temporary variable to keep track of.

The parts appear in a fixed order. First the expression that produces each element, then one or more for clauses, then an optional if that filters. The if comes last and belongs to the loop; it decides whether an item is used at all. A conditional expression inside the leading part, "free" if cell == 0 else "taken", is different: it chooses between two values for every item and must have an else.

Which brackets you use decides what you build. Square brackets make a list. Curly braces with key: value make a dictionary; curly braces with a single expression make a set. Parentheses do not make a tuple: (x for x in items) is a generator expression, which produces items lazily one at a time and is covered in the generators lesson. Pass one directly to sum(), max() or any() when you never need the whole list.

Comprehensions can nest. Two for clauses read left to right exactly as the equivalent nested loops would be written, so [cell for row in grid for cell in row] walks each row, then each cell in it. The leading expression can itself be a comprehension, which is how a grid is read from input: [[int(x) for x in input().split()] for _ in range(rows)].

The loop variable of a comprehension is local to it and does not leak into the surrounding scope, unlike the variable of a plain for statement. The reason to use a comprehension is readability rather than speed: it says what the result is, whereas a loop says how to build it.

Syntax

 Python · syntax
[expr for item in iterable]                  # list
[expr for item in iterable if cond]          # filtered list
[a if cond else b for item in iterable]      # choose a value per item
{key_expr: value_expr for item in iterable}  # dict
{expr for item in iterable}                  # set
(expr for item in iterable)                  # generator, not a tuple
[expr for row in rows for cell in row]       # nested loops, outer first

Any iterable works on the right of for: a list, a string, a range, a file object or a dictionary's items().

Filtering and transforming a list

Parcel weights are filtered, formatted and doubled. The loop at the end builds the same list the long way to show the two are equivalent.

 Python
weights = [2.5, 14.0, 7.4, 30.5, 0.8, 19.9]

heavy = [w for w in weights if w > 10]
print(heavy)

labels = [f"{w:.1f} kg" for w in weights]
print(labels[:3])

doubled = [w * 2 for w in weights]
print(doubled)

result = []
for w in weights:
    if w > 10:
        result.append(w)
print(result == heavy)

Output

[14.0, 30.5, 19.9]
['2.5 kg', '14.0 kg', '7.4 kg']
[5.0, 28.0, 14.8, 61.0, 1.6, 39.8]
True

The first comprehension keeps items unchanged and filters; the second transforms every item into a string and filters nothing; the third transforms numerically. Each corresponds to a loop with an if, an append, or both, and the explicit loop at the end produces an equal list. Every comprehension keeps the order of the source.

Dictionary and set comprehensions

A price list is turned into a tax-inclusive dictionary, a set of cheap items, an inverted lookup and a dictionary of name lengths.

 Python
prices = {"loaf": 3.2, "roll": 0.6, "bagel": 1.1, "cake": 12.0}

with_tax = {name: round(p * 1.1, 2) for name, p in prices.items()}
print(with_tax)

cheap = {name for name, p in prices.items() if p < 2}
print(sorted(cheap))

by_price = {p: name for name, p in prices.items()}
print(by_price[0.6])

lengths = {name: len(name) for name in prices}
print(lengths)

Output

{'loaf': 3.52, 'roll': 0.66, 'bagel': 1.21, 'cake': 13.2}
['bagel', 'roll']
roll
{'loaf': 4, 'roll': 4, 'bagel': 5, 'cake': 4}

Iterating over prices.items() and unpacking name, p gives both halves of each entry. The dictionary comprehension keeps the keys and computes new values; the set comprehension keeps only names and drops the values, so it is printed through sorted() for a stable order. Swapping the roles of key and value builds a reverse lookup, which only works because the prices are distinct. Iterating over a dictionary directly, as in lengths, yields its keys.

Nested comprehensions on a grid

A seating grid is read from input, flattened, labelled with a conditional expression and totalled by column.

 Python
rows = int(input())
grid = [[int(x) for x in input().split()] for _ in range(rows)]
print(grid)

flat = [cell for row in grid for cell in row]
print(flat)

status = ["free" if cell == 0 else "taken" for cell in flat]
print(status.count("taken"))

column_totals = [sum(row[i] for row in grid) for i in range(len(grid[0]))]
print(column_totals)

Input given to the program: 30 1 11 0 00 0 1

Output

[[0, 1, 1], [1, 0, 0], [0, 0, 1]]
[0, 1, 1, 1, 0, 0, 0, 0, 1]
4
[1, 1, 2]

The grid is a comprehension whose element expression is another comprehension: the outer one runs once per row, the inner one converts each token on that line. flat uses two for clauses in the same comprehension, outer row first, then cell. status shows a conditional expression choosing one of two labels for every cell. The column totals pass a generator expression to sum(), so no intermediate list is built for each column.

Which brackets build what

SyntaxBuildsResult
[n * n for n in range(3)]list[0, 1, 4]
{n: n * n for n in range(3)}dict{0: 0, 1: 1, 2: 4}
{n % 2 for n in range(3)}set{0, 1}
(n * n for n in range(3))generatora generator object; wrap in list() to see the values

Common mistakes

  • Putting the filter before the loop

    Why it goes wrong: [x if x > 0 for x in nums] is a syntax error. A filter is an if after the for clause; an if before the for is a conditional expression and needs an else.

    Fix: Write [x for x in nums if x > 0] to filter, or [x if x > 0 else 0 for x in nums] to replace negative values.

  • Using a comprehension only for its side effects

    Why it goes wrong: [print(x) for x in items] builds a list of None values that is thrown away. Readers expect a comprehension to produce a value, so this hides the intent.

    Fix: Use a plain for loop when the body does something rather than computes something.

  • Nesting until nobody can read it

    Why it goes wrong: A comprehension with three for clauses, two filters and a conditional expression fits on one line but takes minutes to decode; the compactness stops paying for itself.

    Fix: Split it into named steps or use a loop. A comprehension should be readable aloud in one breath.

  • Expecting parentheses to give a tuple

    Why it goes wrong: (x * 2 for x in nums) is a generator expression. It cannot be indexed, has no length, and can be consumed only once.

    Fix: Wrap it: tuple(x * 2 for x in nums).

     Python · fix
    pairs = tuple((n, n * n) for n in range(3))
    print(pairs)   # ((0, 0), (1, 1), (2, 4))

Where you use this

Data clean-up is where comprehensions shine. Lines read from a file arrive with trailing newlines and blank lines mixed in; [line.strip() for line in lines if line.strip()] gives you clean, non-empty lines in one step. A list of prices as strings becomes numbers with [float(p) for p in raw], a list of records becomes a lookup table with {r["id"]: r for r in records}, and a column is pulled out of a grid with [row[2] for row in grid].

Aggregation pairs with generator expressions. sum(order["total"] for order in orders if order["paid"]) adds up paid orders without building an intermediate list, and any(len(name) > 30 for name in names) stops at the first long name it finds.

 Python · in practice
clean = [line.strip() for line in lines if line.strip()]
by_id = {r["id"]: r for r in records}
total = sum(o["total"] for o in orders if o["paid"])

Key points

  • A comprehension is an expression that builds a collection: element expression, then for clauses, then an optional if filter.
  • Square brackets make a list, {k: v ...} a dictionary, {v ...} a set, and parentheses a generator.
  • A conditional expression in the element position needs an else; a filter after the for does not.
  • Nested for clauses read in the same order as nested loops, outer loop first.
  • The loop variable does not leak out of the comprehension.
  • Use a plain loop when the body performs actions or when the comprehension stops being readable.

Try it yourself

The comprehension currently collects the squares of every number from 1 to n. Add a filter so it keeps only the squares of the odd numbers, then print the list.

Your program
n = int(input())
squares = [k * k for k in range(1, n + 1)]
print(squares)
Input the program receives: 7
Expected output: [1, 9, 25, 49]

Practise this

Exercises for this lesson are in the Python practice set.

Open the Python playground

Frequently asked questions

Is a list comprehension faster than a for loop?

Usually a little, because the append and the loop bookkeeping are handled inside the interpreter rather than as separate bytecode for each item, and Python 3.12 inlines comprehensions to remove one more function call. The difference is small compared with the readability gain, so choose a comprehension because it states the result clearly, not for speed.

Can a list comprehension have an else?

Only as part of a conditional expression in the element position: [x if x > 0 else 0 for x in nums]. The filtering if after the for clause cannot have an else, because it decides whether to include an item at all rather than which value to produce.

What is the difference between a list comprehension and a generator expression?

A list comprehension, written with square brackets, builds the whole list in memory immediately; you can index it, take its length and loop over it repeatedly. A generator expression, written with parentheses, produces one item at a time on demand, uses almost no memory, and can be consumed only once. Pass a generator expression to sum, max, any or all when you need only the aggregate.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with CPython 3.11 at build time; runs in your browser on CPython 3.14 (Pyodide) by the publishing checks, and the output shown is what it printed. The Run buttons execute the same code on your device; nothing you type is sent anywhere.