Python Part I · Module 9 · Conditional Statements
Conditional Expressions
Every decision so far has chosen which statements to run. A different and narrower need comes up constantly: you do not want to run different code, you want one value, and which value depends on a condition. Writing that with an if and an else takes four lines to express one assignment, and splits a single idea across a block. Python has a form that produces a value directly. It is worth learning precisely rather than as a shorthand, because the thing that decides whether it is the right tool is not how long it is — it is whether you need a value or an action.
Intermediate13 min readPythonPart IFundamentalsConditionalsExpressions
Part I · Module 9 · Chapter 9.6 · Conditional Expressions
1. Four Lines to Set One Name
This is the shape that motivates the chapter:
errors = 2
if errors == 0:
label = "PASS"
else:
label = "FAIL"
print(label)FAILLook at what the branches actually do. They do not run different code — they assign to the same name, differing only in the value. The decision is not about control flow at all; it is about which of two values label should hold.
2. One Line That Produces a Value
Python can say that directly:
errors = 2
label = "PASS" if errors == 0 else "FAIL"
print(label)FAILRead it in the order it is written: the value "PASS", if errors == 0, otherwise the value "FAIL". The whole thing to the right of = is one expression that produces a single value, and label is assigned it in the ordinary way.
errors = 0
label = "PASS" if errors == 0 else "FAIL"
print(label)
print(type(label))PASS
<class 'str'>There is nothing conditional about label afterwards. It holds a string, exactly as if you had typed one.
3. It Is an Expression, and That Is the Point
Calling this "a short if" misses what it does. An expression produces a value, so it can go anywhere a value is legal — and a statement cannot go in any of those places.
Inside a call:
errors = 0
print("verdict:", "PASS" if errors == 0 else "FAIL")verdict: PASSInside a list:
errors = 0
rows = ["PASS" if errors == 0 else "FAIL", "n/a"]
print(rows)['PASS', 'n/a']As the argument to another function:
errors = 0
print(len("PASS" if errors == 0 else "FAIL"))4None of those can be written with an if statement. You cannot put a four-line block inside a list literal. That is the real difference between the two forms, and it is the reason both exist.
| Conditional statement | Conditional expression | |
|---|---|---|
| Written | if c: / else: with blocks | a if c else b |
| Chooses | which statements execute | which value is produced |
| Produces | nothing | a value |
| May appear | as a statement, on its own line | anywhere a value is legal |
| Branches may | do anything, over many lines | be one expression each |
4. Its Actual Name
Python calls this a conditional expression. That is the term in the language reference and the one to use in a review.
You will hear it called a ternary operator, borrowed from C and SystemVerilog, where the same idea is written with ? and ::
label = (errors == 0) ? "PASS" : "FAIL"; // C / SystemVeriloglabel = "PASS" if errors == 0 else "FAIL" # PythonSame three operands, different order. C puts the condition first; Python puts the value first and the condition in the middle, so the line reads as a sentence — this, if that, otherwise the other. If you come from RTL, the reordering is the thing to watch for: the first thing on a Python line is the result, not the test.
5. else Is Not Optional
A conditional statement may have no else — the block is simply skipped. A conditional expression may not:
errors = 0
label = "PASS" if errors == 0 File "bad.py", line 3
label = "PASS" if errors == 0
^^^^^^^^^^^^^^^^^^^^^
SyntaxError: expected 'else' after 'if' expressionThe message names the rule. An expression must produce a value on every path — there is no such thing as an expression that sometimes produces nothing, because label has to be assigned something. A statement can decline to act; an expression cannot decline to have a value.
6. Only the Chosen Value Is Computed
The condition is evaluated first, and only the selected side is computed. The other side is never touched:
errors = 0
print("PASS" if errors == 0 else 1 / 0)
print("the untaken value was never computed")PASS
the untaken value was never computed1 / 0 sits in the expression and does not raise, because errors == 0 was true and Python never evaluated the alternative.
7. The Readability Limit
A conditional expression may contain another one. The result is legal and hard to read:
errors = 7
band = "clean" if errors == 0 else "minor" if errors < 10 else "major"
print(band)minorIt is correct. Working out why requires noticing that the second if belongs to the first else, which means re-parsing the line from the middle. Three outcomes on one line have no visual structure to guide a reader — and this is the two-level case, which is the mildest one.
Written as a chain, the same logic states its own shape:
errors = 7
if errors == 0:
band = "clean"
elif errors < 10:
band = "minor"
else:
band = "major"
print(band)minorThree branches, three lines, each with its own condition in the left margin where the eye already is. Chapter 9.1's reachability check — is each condition narrower than the ones above it — can be applied to the second version by reading down the page, and to the first only by taking it apart.
8. When the Expression Form Cannot Be Used
The limit is not only readability. Some branches cannot be written as expressions at all, because they do not produce values:
errors = 2
if errors == 0:
print("clean run")
else:
print("errors found:", errors)
print("opening the log")errors found: 2
opening the logThose branches act: one prints one thing, the other prints two different things. There is no value being chosen, and the else branch has two statements in it. No conditional expression can express that, and trying to force one would mean inventing a value nobody wants.
The honest division:
- branches that assign the same name, or compute one result → conditional expression
- branches that do different things, or do several things → conditional statement
9. The Precedence Trap
One mistake is worth knowing because it breaks asymmetrically — and asymmetric bugs survive testing.
clean = True
print("PASS" if clean else "FAIL" + " (see log)")PASSThe suffix is gone. + binds more tightly than the conditional expression, so Python read the line as "PASS" if clean, otherwise "FAIL" + " (see log)" — the suffix was only ever attached to the false branch.
Now run the same line with the other value:
clean = False
print("PASS" if clean else "FAIL" + " (see log)")FAIL (see log)That one looks right. The bug is invisible on this branch and visible only on the other, so a quick check that happens to exercise a failing run reports no problem at all.
Parentheses say what was meant:
clean = True
print(("PASS" if clean else "FAIL") + " (see log)")PASS (see log)clean = False
print(("PASS" if clean else "FAIL") + " (see log)")FAIL (see log)10. Predict, Run, Observe
# A
errors = 0
print("PASS" if errors == 0 else "FAIL")
# B
failed = []
print("none" if not failed else "some")
# C
count = 0
print("zero" if count == 0 else "nonzero")
# D
width = 32
print(("wide" if width > 16 else "narrow") + " bus")The verified output:
PASS
none
zero
wide busA — the condition is true, so the first value is produced.
B — failed is empty, so not failed is true (Chapter 9.5), giving "none".
C — count == 0 is an explicit comparison rather than a truthiness test, which is deliberate: "zero" if count else ... would be backwards, because 0 is falsy.
D — the parentheses make the conditional expression produce one word, and the concatenation happens after. Without them, " bus" would have attached only to "narrow".
11. Exercises
Exercise 1 — convert both ways. Rewrite each as the other form: (a) label = "ok" if n < 10 else "high" (b) an if/else that sets sign to "+" when value >= 0 and "-" otherwise.
Exercise 2 — which form. For each, say whether a conditional expression or a conditional statement is correct, in one sentence: (a) set verdict to "PASS" or "FAIL" (b) print one message or two, depending on the error count (c) choose between two separators for a report line (d) either append to a list or print a warning.
Exercise 3 — the missing else. Write status = "PASS" if clean and record the exact error. Explain in two sentences why the statement form is allowed to omit else and the expression form is not.
Exercise 4 — nested, then unnested. Take grade = "A" if s >= 90 else "B" if s >= 80 else "C" and rewrite it as a chain. Then check with Chapter 9.1's reachability rule whether every branch of your version can be reached, and say which form made that easier to check.
Exercise 5 — the asymmetric bug. Write print("ok" if flag else "bad" + "!") and run it with flag = True and with flag = False. Record both outputs, say which one reveals the bug, and explain in one sentence why this is dangerous.
Exercise 6 — inside a value. Using one conditional expression, build the list ["PASS", "reset"] when errors == 0 and ["FAIL", "reset"] otherwise. Then say why this cannot be written with an if statement inside the list literal.
Exercise 7 — the untaken side. Write a one-line program that would raise if Python evaluated both sides of a conditional expression, and show that it does not. Name the two earlier places in this module where the same mechanism appeared.
Exercise 8 — judge the line. For each, say whether you would keep it or rewrite it, and why: (a) n = count if count > 0 else 0 (b) msg = "ok" if a else "warn" if b else "bad" if c else "fatal" (c) width = widths["clk"] if "clk" in widths else 1 (d) print("y") if ok else print("n").
12. Summary
- A conditional expression produces one of two values:
a if condition else b. - It is an expression, not a short statement. It can appear anywhere a value is legal — inside a call, a list literal, or another expression — where a block cannot.
- A conditional statement chooses which statements run; a conditional expression chooses which value you get. That is the question that decides which to write.
- Its name is "conditional expression". The C and SystemVerilog
?:is the same idea with the condition first instead of in the middle. elseis mandatory, because an expression must produce a value on every path. Omitting it is aSyntaxError.- Only the chosen side is computed — the third appearance of the one rule that Python evaluates no more than it needs.
- One condition and two simple values is the limit. A third outcome belongs in an
if/elif/elsechain, which states its own structure. - Branches that act rather than produce a value cannot use this form at all.
- Parenthesise it whenever it is combined with another operator.
+binds tighter, and the resulting bug shows up on only one of the two branches.
The next chapter steps back from syntax to correct an assumption that RTL experience makes almost inevitable: what an if actually does when the program runs.
Where this fits
Part of the Python curriculum.
