Python List Comprehension If Else: Where Each One Goes

In a Python list comprehension, if else goes before the for, and a bare if goes after it. Get that backwards and Python raises a SyntaxError.

labels = ["pass" if s >= 50 else "fail" for s in scores]   # if/else before the for
passed = [s for s in scores if s >= 50]                    # bare if after the for

The first one labels every item. The second one throws items away. That single difference is what most of the questions about this are really about.

I ran each snippet on Python 3.12.5 and screenshotted the Command Prompt, so the output you see is the output I got.

Where if else goes in a list comprehension

Those are two different pieces of syntax that happen to share a keyword.

"pass" if s >= 50 else "fail" is a conditional expression. It’s a value, so it belongs where a value belongs: at the front, where the comprehension decides what to put in the list.

The trailing if s >= 50 is a filter clause. It’s part of the loop, so it sits with the loop, after the for.

scores = [92, 45, 78, 61, 30]

# if/else goes BEFORE the for: every score produces a value
labels = ["pass" if s >= 50 else "fail" for s in scores]
print(labels)

# a bare if goes AFTER the for: it filters, so the list gets shorter
passed = [s for s in scores if s >= 50]
print(passed)

print("in:", len(scores), "| labelled:", len(labels), "| filtered:", len(passed))

Output:

['pass', 'fail', 'pass', 'pass', 'fail']
[92, 78, 61]
in: 5 | labelled: 5 | filtered: 3
Command Prompt showing a Python list comprehension with if else labelling five scores and a bare if filtering the same list down to three
Five in, five labelled, three kept. Same data, two jobs.

Count the outputs and the rule stops being abstract. The conditional expression kept the length at five. The filter cut it to three.

What you wantWhere the condition goesExample
A value for every itemBefore the for[a if cond else b for x in xs]
Only some itemsAfter the for[x for x in xs if cond]
BothBoth places[a if c1 else b for x in xs if c2]
More than two outcomesChain, or call a function[f(x) for x in xs]

Why your if else gives a SyntaxError

Put the else after the for and Python stops before it runs a single line:

scores = [92, 45, 78, 61, 30]

# the same if/else, but placed after the for
labels = [s for s in scores if s >= 50 else "fail"]
print(labels)

What Python says:

  File "C:\pyguides\list_comp_syntax_error.py", line 4
    labels = [s for s in scores if s >= 50 else "fail"]
                                           ^^^^
SyntaxError: invalid syntax
Command Prompt showing the SyntaxError Python raises when an if else is placed after the for in a list comprehension
The caret points at else, because nothing is allowed there.

The filter clause has no else branch to offer. Python has already finished reading a valid comprehension by the time it meets that word, so it has nowhere to put it.

The fix is always the same: decide whether you’re labelling or filtering, then move the condition to the matching end.

List comprehension if with no else (“do nothing”)

This is the most common follow-up, and the short answer is that you can’t. A conditional expression must produce a value, so the else is not optional.

What you actually want is the filter form:

values = [4, -2, 9, -7, 0]

# there is no "and otherwise do nothing" in a conditional expression.
# dropping the else is a SyntaxError, so move the test after the for instead:
kept = [v for v in values if v > 0]
print(kept)

# if you want a placeholder rather than a shorter list, say so out loud
marked = [v if v > 0 else None for v in values]
print(marked)
print([v for v in marked if v is not None])

Output:

[4, 9]
[4, None, 9, None, None]
[4, 9]

If you’d rather keep the positions and mark the gaps, put something explicit in the else and strip it afterwards. That’s the second half of the output above.

Filter and transform in one list comprehension

Both conditions can appear in the same comprehension, and this is where the order finally pays off:

readings = [12.4, None, -3.0, 40.1, None, 8.0]

# filter with the if after the for, transform with the if/else before it
cleaned = [round(r) if r > 0 else 0 for r in readings if r is not None]
print(cleaned)

# read it in the order Python runs it:
#   for r in readings -> if r is not None -> round(r) if r > 0 else 0
print(len(readings), "readings in,", len(cleaned), "out")

Output:

[12, 0, 40, 8]
6 readings in, 4 out
Command Prompt showing a Python list comprehension that filters out None readings and then rounds or zeroes the remaining values
Six readings in, four out: the Nones were filtered, the rest were transformed.

Read it in the order Python runs it, not the order you see it: the for first, then the filter, then the expression. Dropping None before doing arithmetic is the usual reason to bother, and it pairs with checking whether a variable is None.

Chaining if else for more than two outcomes

There’s no elif in a comprehension. You chain conditional expressions, and each else holds the next test:

scores = [92, 74, 58, 31]

# there is no elif in a comprehension: you chain conditional expressions
grades = ["A" if s >= 90 else "B" if s >= 70 else "C" if s >= 50 else "F" for s in scores]
print(grades)

# the same thing, but you can read it and test it
def grade(score):
    if score >= 90:
        return "A"
    if score >= 70:
        return "B"
    if score >= 50:
        return "C"
    return "F"

print([grade(s) for s in scores])

Output:

['A', 'B', 'C', 'F']
['A', 'B', 'C', 'F']

Three branches is where I’d stop. The chained version and the function below it produce the same grades, but only one of them can be read at a glance or unit-tested on its own.

Once the logic needs a name, give it one. A named function inside the comprehension keeps the line short and the intent obvious, which is the same reason a sort key gets pulled into its own function.

Is a list comprehension faster than a for loop?

A little, and for the usual reason: the comprehension builds the list in C instead of calling append twenty thousand times.

import timeit

setup = "scores = list(range(20_000))"
comp = "[s if s % 2 else -s for s in scores]"
loop = """
out = []
for s in scores:
    out.append(s if s % 2 else -s)
"""

for name, stmt in (("comprehension", comp), ("for loop + append", loop)):
    ms = timeit.timeit(stmt, setup, number=200) / 200 * 1000
    print(f"{name:<18} {ms:6.2f} ms per run")

Output:

comprehension        0.58 ms per run
for loop + append    0.67 ms per run
Command Prompt comparing the run time of a Python list comprehension against an equivalent for loop with append
Same work, same result, measured over 200 runs each.

That gap is real but small. Pick the comprehension because it reads better on one line, not because of the milliseconds.

When not to use a list comprehension

A comprehension stops paying for itself the moment you have to re-read it.

  • The condition needs try/except, which a comprehension can’t hold.
  • You’re chaining more than two or three conditional expressions.
  • The line runs past the edge of the screen and wraps.
  • You’re doing something rather than building something, like printing or writing to a file.

A plain for loop is not a lesser tool. It’s the right one whenever the logic is worth a few extra lines, and it handles the cases a lambda inside a comprehension would only make denser.

If you’re working through comprehensions, these go well next:

Frequently asked questions

Where does if else go in a Python list comprehension?

Before the for. It’s a conditional expression, so it sits where the value goes: [a if cond else b for x in xs]. The Python tutorial on list comprehensions sets out the same grammar.

Why does my list comprehension if else give a SyntaxError?

Because the else is after the for. The filter clause there has no else branch, so move the whole condition to the front.

Can I use if without else in a list comprehension?

Yes, but only as a filter after the for. A conditional expression at the front always needs both branches.

How do I do nothing in the else?

You can’t leave it empty. Either filter with a trailing if, or put a placeholder such as None in the else and remove it later.

Is there an elif in list comprehension?

No. Chain conditional expressions instead, or move the logic into a small function and call it from the comprehension.

Can I use multiple if conditions after the for?

Yes. Several trailing if clauses are combined with and, so [x for x in xs if x > 0 if x % 2] keeps only positive odd numbers.

Is a list comprehension faster than a for loop?

Slightly, because it avoids repeated append calls. The difference is milliseconds on twenty thousand items, so readability should decide.