What is it? #
A comprehension is a compact way to build a new collection from an existing one. It replaces the common pattern of creating an empty list, looping, and appending.
The shape is always the same: what you want, where it comes from, and optionally a condition that filters it.
There are three flavours: list comprehensions with [], set comprehensions with {}, and dict comprehensions with {key: value}. Generator expressions use () and produce items lazily, which matters for large data.
Comprehensions are idiomatic Python, but they are not automatically better. One line with two conditions and a nested loop is harder to read than the five-line version, and readability wins.
Think of it like this #
A comprehension is like telling someone "give me the ripe apples from this crate" instead of "take an empty basket, pick up each apple, check if it is ripe, if it is put it in the basket, repeat". Same result, one sentence.
Simple example #
You have a list of order dictionaries. You want the IDs of paid orders over 1000, a lookup of order ID to amount, and the set of unique cities involved.
Code #
orders = [
{"id": "A1", "amount": 1500, "paid": True, "city": "Pune"},
{"id": "A2", "amount": 700, "paid": True, "city": "Delhi"},
{"id": "A3", "amount": 2400, "paid": False, "city": "Pune"},
{"id": "A4", "amount": 1100, "paid": True, "city": "Surat"},
]
# The loop version
big_paid = []
for order in orders:
if order["paid"] and order["amount"] > 1000:
big_paid.append(order["id"])
# The comprehension version
big_paid = [o["id"] for o in orders if o["paid"] and o["amount"] > 1000]
print(big_paid) # ['A1', 'A4']
# Dict comprehension
amounts = {o["id"]: o["amount"] for o in orders}
print(amounts) # {'A1': 1500, 'A2': 700, ...}
# Set comprehension
cities = {o["city"] for o in orders}
print(cities) # {'Pune', 'Delhi', 'Surat'}
# Transform while filtering
with_tax = [round(o["amount"] * 1.18) for o in orders if o["paid"]]
print(with_tax) # [1770, 826, 1298]
# Generator expression — lazy, no list built
total = sum(o["amount"] for o in orders if o["paid"])
print(total) # 3300
How it works #
Read [o["id"] for o in orders if o["paid"] and o["amount"] > 1000] from the middle outwards. for o in orders is the source, if ... filters, and o["id"] is what ends up in the new list. It is exactly the loop above it, written in the order you would say it out loud.
The dict comprehension uses key: value before the for. Everything else works the same, and the result is a dictionary.
The set comprehension has the same braces as a dict but no colon, so Python builds a set and duplicates disappear — Pune appears once even though two orders came from there.
round(o["amount"] * 1.18) shows that the left part can be any expression, not just a plain field. Transformation and filtering happen in one pass.
The last line is a generator expression: no brackets, just the expression inside the call to sum(). It produces values one at a time instead of building an intermediate list. For four orders that saves nothing; for four million it saves a lot of memory.
Real-world use #
Comprehensions are everywhere in Python code that shapes data: extracting IDs to pass to a second query, converting database rows into response objects, building a lookup dictionary before a loop so you can avoid repeated searching.
That last pattern is a common performance fix. Instead of searching a list for a matching record inside a loop, build {row.id: row for row in rows} once and look up by key. It turns a slow nested scan into a fast lookup.
Generator expressions matter when you pipe large data through sum, any, all or max. You get the answer without holding the whole intermediate collection in memory.
Common mistakes #
- Cramming nested loops and multiple conditions into one comprehension. If you have to read it twice, write the loop.
- Using a comprehension purely for side effects, like calling print inside one. Use a normal for loop for actions.
- Building a huge list when a generator expression would do, then running out of memory.
- Forgetting that the filter
ifgoes after thefor, while a conditional expression (x if cond else y) goes before it. - Shadowing an outer variable with the loop name and confusing yourself later.
Practice #
From nums = [4, 9, 15, 22, 31, 40], build a list of squares of the even numbers, a dictionary mapping each number to whether it is divisible by 5, and a set of the remainders when divided by 3. Then compute the sum of all numbers over 10 using a generator expression.