Skip to content
VLSI Mentor

Python Part I · Module 12 · Comprehensions

Conditional Expressions Inside Comprehensions

One keyword appears in two places inside a comprehension and means two completely different things. At the end it is a filter, and it decides whether an item appears in the result at all. At the front it is a conditional expression, and it decides what an item turns into while leaving the count alone. They look similar enough that experienced engineers mix them up, and the usual symptom is not an error — it is a result with the wrong number of rows in it. This chapter separates them with one pair of programs and one number, and that number is the reliable way to tell which one you have written.

Intermediate15 min readPythonPart IFundamentalsComprehensionsConditionals

Part I · Module 12 · Chapter 12.4 · Conditional Expressions Inside Comprehensions

1. Two Things Named if

You have met both already, in different chapters.

The filter clause — Chapter 12.1, Section 7. It sits after the source and decides which items reach the output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[r["test"] for r in results if r["status"] == "FAIL"]
                              ─────────────────────
                                   the filter

The conditional expression — Chapter 9.6. It is an expression that produces one of two values:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
"PASS" if errors == 0 else "FAIL"

Put the second inside a comprehension and the result is a line with two ifs that mean unrelated things. Keeping them apart is this chapter's whole job.

2. The Data

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
results = [
    {"test": "axi_smoke", "status": "PASS"},
    {"test": "axi_burst", "status": "FAIL"},
    {"test": "reset_seq", "status": "PASS"},
]

print(len(results))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3

Three results, two of which passed.

3. The Pair That Settles It

A filter — the if is at the end:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
results = [
    {"test": "axi_smoke", "status": "PASS"},
    {"test": "axi_burst", "status": "FAIL"},
    {"test": "reset_seq", "status": "PASS"},
]

passed = [r["test"] for r in results if r["status"] == "PASS"]

print(passed)
print(len(passed), "of", len(results))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['axi_smoke', 'reset_seq']
2 of 3

A conditional expression — the if is at the front, and there is an else:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
results = [
    {"test": "axi_smoke", "status": "PASS"},
    {"test": "axi_burst", "status": "FAIL"},
    {"test": "reset_seq", "status": "PASS"},
]

verdicts = ["ok" if r["status"] == "PASS" else "investigate" for r in results]

print(verdicts)
print(len(verdicts), "of", len(results))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['ok', 'investigate', 'ok']
3 of 3

Two of three against three of three. Same source, same condition, same keyword — and one answers which tests passed while the other answers what to do about each test.

4. Position Is the Rule

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[  "ok" if passed else "bad"   for r in results   if r["status"] == "FAIL"  ]
   ──────────────────────────  ────────────────   ────────────────────────
     CONDITIONAL EXPRESSION         SOURCE                  FILTER
         (the output)                                  (drops items)
  • Before the for — part of the output expression. It must have an else, because Chapter 9.6 established that an expression must produce a value on every path.
  • After the source — a filter clause. It must not have an else, because a clause is not an expression and there is nothing for an else to produce.

That is the entire distinction, and it is positional rather than semantic. The same words in the two slots do different jobs.

WhereelseChanges the countWhat it does
conditional expressionbefore forrequirednopicks one of two values
filter clauseafter the sourcenot allowedyesdrops items

5. The Mistake Everyone Makes

Trying to put an else on the filter:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
results = [{"test": "a", "status": "PASS"}]

labels = [r["test"] for r in results if r["status"] == "PASS" else "skipped"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
SyntaxError: invalid syntax

Python refuses before anything runs. A filter clause takes no else — there is no value for it to produce, because a filter does not produce values at all. It answers yes or no about an item the output expression has already been written for.

If what you wanted was a label for every item, which differs by status, the condition belongs at the front:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
results = [
    {"test": "a", "status": "PASS"},
    {"test": "b", "status": "FAIL"},
]

labels = [r["test"] if r["status"] == "PASS" else "skipped" for r in results]

print(labels)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['a', 'skipped']

Two items in, two out — the failing one became "skipped" rather than disappearing.

6. Both in One Comprehension

They occupy different slots, so they can coexist:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
results = [
    {"test": "axi_smoke", "status": "PASS", "sim": "vcs"},
    {"test": "axi_burst", "status": "FAIL", "sim": "vcs"},
    {"test": "reset_seq", "status": "PASS", "sim": "xrun"},
]

vcs_verdicts = ["ok" if r["status"] == "PASS" else "investigate"
                for r in results
                if r["sim"] == "vcs"]

print(vcs_verdicts)
print(len(vcs_verdicts), "of", len(results))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['ok', 'investigate']
2 of 3

Read it in two passes. The filter cut three results to the two run under vcs. The conditional expression then turned each of those two into a word. One clause changed the count; the other changed the contents.

Written across three lines as above, with the source and the filter on their own, it stays readable. On one line it would not, which is Section 7's point.

7. When to Stop

A conditional expression in a comprehension is one condition's worth of complexity on top of a construct that already has three parts. Two guidelines, both from chapters you have:

Do not nest conditional expressions. Chapter 9.6 Section 7 ruled this out on its own; inside a comprehension it is worse:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
errors = [0, 5, 50]

bands = ["clean" if n == 0 else "minor" if n < 10 else "major" for n in errors]

print(bands)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['clean', 'minor', 'major']

That is correct and it is at the limit. A third level would need re-parsing from the middle, and the answer there is a named function — Chapter 11.2's — called from the output expression:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
def band(n):
    """Return the severity band for an error count."""
    if n == 0:
        return "clean"
    if n < 10:
        return "minor"
    if n < 100:
        return "major"
    return "critical"


errors = [0, 5, 50, 500]

print([band(n) for n in errors])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['clean', 'minor', 'major', 'critical']

The comprehension is now one short line that says what it does, the branching lives somewhere it can have a docstring, and band can be checked on its own — Chapter 11.15's whole argument.

Break the line when both forms appear. The three-line layout in Section 6 is not a formatting preference; a filter and a conditional expression on one line put two different ifs within a few characters of each other, which is the exact confusion this chapter exists to prevent.

8. Predict, Run, Observe

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
nums = [1, 2, 3, 4]

# A
print("A:", [n for n in nums if n % 2 == 0])

# B
print("B:", ["even" if n % 2 == 0 else "odd" for n in nums])

# C
print("C:", [n * 10 if n % 2 == 0 else n for n in nums])

# D
a = [n for n in nums if n > 2]
b = ["big" if n > 2 else "small" for n in nums]
print("D:", len(a), len(b))

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
A: [2, 4]
B: ['odd', 'even', 'odd', 'even']
C: [1, 20, 3, 40]
D: 2 4

A — a filter: two of four.

B — a conditional expression: four of four, each replaced by a word.

C — a conditional expression where one branch transforms and the other passes the item through unchanged. Still four items; the odd ones were kept as they were.

D — the two lengths side by side. 2 and 4 from the same source is the whole chapter.

9. Exercises

Exercise 1 — which is it. For each, say filter or conditional expression, and give the output length for a four-item source: (a) [x for x in xs if x > 0] (b) [x if x > 0 else 0 for x in xs] (c) [abs(x) for x in xs] (d) [x for x in xs if x].

Exercise 2 — the pair. Using a three-result list, write both the filter version and the conditional-expression version of "which tests passed". Print both results and both lengths, and say which answers which question.

Exercise 3 — the SyntaxError. Write a filter with an else on it and record the exact message. Explain in two sentences why Python cannot accept it.

Exercise 4 — move it to the front. Take your Exercise 3 line and rewrite it so every item produces a value, with failures becoming "skipped". Confirm the length now equals the source's.

Exercise 5 — both. Write one comprehension that keeps only the xrun runs and labels each as "ok" or "investigate". Lay it out across three lines and say why.

Exercise 6 — clamp. Given a list of runtimes, build a list where anything over 600 becomes exactly 600 and everything else is unchanged. Say which form you used and why the count did not change.

Exercise 7 — past the limit. Write a three-level nested conditional expression inside a comprehension, then rewrite it with a named function. Say what the function version gained besides length.

Exercise 8 — read the count. A colleague's comprehension over 40 results returns 40 items. Without seeing it, what can you conclude about its if? What if it returned 31?

10. Summary

  • Two different things are spelled if inside a comprehension, and position tells them apart.
  • Before the for it is a conditional expression — part of the output, requires an else, picks one of two values.
  • After the source it is a filter clause — no else allowed, decides whether an item appears at all.
  • A filter changes the count; a conditional expression never does. That is the reliable discriminator.
  • An else on a filter is a SyntaxError, caught before anything runs — the language enforcing the positional rule.
  • Both can appear in one comprehension, because they occupy different slots.
  • Lay that version out across lines. Two unrelated ifs on one line is the confusion this chapter exists to prevent.
  • Do not nest conditional expressions — two levels is the limit, and past it a named function is the answer.
  • A named function in the output expression keeps the comprehension short, gives the branching a docstring, and makes it checkable on its own.

The next chapter adds the third collection type, and the one job it does better than either of the others.

Where this fits

Part of the Python curriculum.