Day 3 · ~16m

List Comprehensions in Python: Replace For-Loops with Expressions

Learn Python list comprehensions to filter and transform data in one readable line. Discover when they beat for-loops — and the exact point where they don't.

student (focused)

I wrote this last week when Amir asked me to pull the paid orders before the daily report runs. It works fine, I already tested it.

teacher (neutral)

Show me.

student (neutral)

Here — classic loop, nothing fancy:

teacher (neutral)
orders = [
    {"id": 101, "customer": "Alice Chen", "total": 124.50, "status": "paid"},
    {"id": 102, "customer": "Bob Kumar", "total": 89.00, "status": "pending"},
    {"id": 103, "customer": "Carol Santos", "total": 340.00, "status": "paid"},
    {"id": 104, "customer": "Dev Patel", "total": 15.99, "status": "cancelled"},
]

paid_orders = []
for order in orders:
    if order["status"] == "paid":
        paid_orders.append(order)

print(paid_orders)
teacher (focused)

It works. Now here's what Amir probably has in his version:

paid_orders = [order for order in orders if order["status"] == "paid"]
student (thinking)

One line. But that's... I mean, it does the same thing, right? Is this just showing off?

teacher (amused)

I had a senior dev ask me that exact question in 2011. In a code review. About my code.

student (amused)

What did you say?

teacher (neutral)

I said "yes" and rewrote it as a loop. Then six months later I came back around.

student (curious)

Okay. So what changed?

teacher (serious)

Read the comprehension out loud, exactly as it's written.

student (thinking)

"order... for order in orders... if order status is paid." Huh. That's actually English.

teacher (neutral)

Compare that to the loop. You've got an empty list, an append call, and an if — three separate ideas spread across four lines. The comprehension is one idea: "give me orders from this list, paid ones only." Python lets you write the result description instead of the construction steps.

student (curious)

So the structure is always — the thing you want first, then where it comes from, then the filter?

teacher (focused)

Exactly. Three parts, always in that order:

# [what_you_want   for item in collection   if condition]
#  ─────────────   ──────────────────────   ────────────
#  expression      loop clause              filter (optional)

# Just transformation — no filter:
customer_names = [order["customer"] for order in orders]
# ['Alice Chen', 'Bob Kumar', 'Carol Santos', 'Dev Patel']

# Just filtering — expression == item itself:
paid_orders = [order for order in orders if order["status"] == "paid"]

# Both — transform AND filter:
paid_totals = [order["total"] for order in orders if order["status"] == "paid"]
# [124.5, 340.0]
student (thinking)

Okay I can see using it for simple stuff. But if it gets any more complicated than that, I'm writing a loop. Readability matters.

teacher (neutral)

Fair. Let me show you where the loop actually gets harder to read, and you tell me which is clearer.

student (focused)

Go ahead.

teacher (serious)

Say we have a list of products, and each product has a list of warehouse locations. We want all the warehouse codes for active products with stock:

products = [
    {"sku": "SHOE-01", "active": True,  "warehouses": ["SYD", "MEL"]},
    {"sku": "BAG-07",  "active": False, "warehouses": ["SYD"]},
    {"sku": "HAT-03",  "active": True,  "warehouses": ["BNE", "SYD", "PER"]},
]

# Loop version:
locations = []
for product in products:
    if product["active"]:
        for warehouse in product["warehouses"]:
            locations.append(warehouse)
# ['SYD', 'MEL', 'BNE', 'SYD', 'PER']

# Comprehension version:
locations = [
    warehouse
    for product in products
    if product["active"]
    for warehouse in product["warehouses"]
]
student (confused)

Wait. There are two for clauses? That's not the three-part structure you showed me.

teacher (encouraging)

You're reading it right — nested comprehensions add a second loop clause. The order mirrors the loop: outer for first, filter, inner for. Read it top-to-bottom: "give me each warehouse, for each product, if it's active, for each warehouse in that product."

student (struggling)

I actually had to read the loop version twice to count the nesting levels. The comprehension version... I hate that it's clearer.

teacher (excited)

That's the exact moment I had. Three-level loop nesting is where comprehensions start earning their keep.

student (thinking)

Okay. So simple filter or transform — either is fine. Once you're nesting, comprehension wins. What about when not to use it?

teacher (focused)

When the expression itself is complex. If you're calling three functions and doing conditional logic inside the expression, you've already lost. Write the loop — add a comment. Comprehensions shine when the what-you-want part stays simple.

# This is readable — expression is just a field lookup:
customer_names = [order["customer"] for order in orders if order["status"] == "paid"]

# This is not — expression has too much going on:
formatted = [
    f"{order['customer'].split()[0]} ({order['total']:.2f} {'USD' if order['currency'] == 'usd' else 'AUD'})"
    for order in orders if order["status"] == "paid" and order["total"] > 50
]
# Write that as a loop with a helper function
student (curious)

So there's a readability threshold. Below the line — comprehension. Above — loop plus maybe a helper.

teacher (proud)

That's the whole decision rule. Under one conceptual idea: comprehension. Over one: extract the logic, name it, then decide.

student (focused)

Alright. Let me try it on product data. Active products with a real price — not zero, not negative:

teacher (neutral)
products = [
    {"sku": "SHOE-01", "name": "Running Shoe",  "price": 89.99,  "active": True},
    {"sku": "BAG-07",  "name": "Tote Bag",       "price": 0.00,   "active": True},
    {"sku": "HAT-03",  "name": "Sun Hat",        "price": 24.50,  "active": False},
    {"sku": "BELT-02", "name": "Leather Belt",   "price": 45.00,  "active": True},
]

listable_products = [p for p in products if p["active"] and p["price"] > 0]
print(listable_products)
# [{'sku': 'SHOE-01', ...}, {'sku': 'BELT-02', ...}]
teacher (encouraging)

And if you only need the names for a dropdown:

dropdown_names = [p["name"] for p in products if p["active"] and p["price"] > 0]
# ['Running Shoe', 'Leather Belt']
student (excited)

Oh — I've been doing this pattern all the time. Build a list of dicts, then loop through it again to pull out one field for the frontend. This is cleaner.

teacher (surprised)

You just described half the code in most e-commerce backends. Pull records, project fields, send to the client. Comprehensions are exactly that pattern.

student (proud)

I'm rewriting two things in today's sprint. Amir's going to think I had a weekend course.

teacher (amused)

Don't tell him it was a Tuesday afternoon.

student (curious)

One thing though — I keep building lists because that's what comprehensions make. But sometimes I need the structure to be a dictionary. Like, order ID mapped to total. Can I do that?

teacher (serious)

Tomorrow. Because what you're describing is a dict comprehension — same idea, different brackets, one extra concept worth its own session. And once you see it, you'll realize it solves a problem you've probably been solving with a loop and three extra lines for months.