PYTHON / CONTROL FLOW
else and nested conditionals
Use else as the catch-all for an if, and nest if/else blocks so that indentation makes each else bind to the test you intended.
What you will learn
- Attach an else to an if so exactly one of the two blocks always runs
- Read indentation to tell which if a given else belongs to
- Nest a conditional when the inner test is only meaningful after the outer passes
- Spot the fall-through case where an outer if is taken but no inner branch matches
Understanding else and nested conditionals
An if statement on its own has one exit that does something and one that does nothing. Adding else gives the second exit a body, so the pair becomes a fork: the interpreter evaluates the condition once, runs one of the two blocks, and never both. That is why you cannot put a condition after else — it is defined as everything the if test did not cover, so a second test there would be either redundant or a contradiction.
Nesting means putting a whole if/else inside the body of another. Python decides which if an else belongs to purely by indentation: an else at four spaces closes the if at four spaces, and an else at column zero closes the if at column zero. Languages with braces have the classic dangling-else ambiguity; Python removes the ambiguity but moves the risk into whitespace, where shifting an else by one level is a silent change of meaning rather than a syntax error.
The useful reason to nest instead of combining tests with and is dependency. If the inner condition would crash or make no sense when the outer one is false — indexing a string only after confirming it is non-empty, reading a key only after confirming the dictionary has it — nesting encodes the required order of checks. Also remember that an outer if with an inner if but no inner else has a path that does nothing at all, which is a common source of variables that were never assigned.
def describe(n):
if n % 2 == 0:
if n % 4 == 0:
return f"{n}: divisible by 4"
else:
return f"{n}: even, not divisible by 4"
else:
return f"{n}: odd"
for value in (7, 10, 12):
print(describe(value))An else belongs to the if at its own indentation level, and nesting exists to express tests that only make sense once an outer test has already passed.
Worked examples
Which if does the else close?
Shows that the outer else runs only for the outer condition, leaving a fall-through path when the outer test passes but the inner one fails.
def check(x, y):
if x > 0:
if y > 0:
return "both positive"
else:
return "x is not positive"
return "x positive, y is not"
print(check(2, 5))
print(check(2, -5))
print(check(-2, 5))Example explained
Line 1The else sits at the same indentation as `if x > 0`, so it handles only x <= 0.
Line 2With x=2, y=-5 the inner if is false and there is no inner else, so control leaves the outer block.
Line 3The final return is at function level, so it catches exactly that fall-through case.
Line 4With x=-2 the outer else returns immediately and the inner if is never evaluated.
Nesting to order dependent checks
Demonstrates a nested if whose test would raise an error if the outer test had not already passed.
def initial(name):
if name is not None:
if len(name) > 0:
return name[0].upper()
else:
return "?"
else:
return "-"
print(initial("ada"))
print(initial(""))
print(initial(None))Example explained
Line 1`len(name)` would raise TypeError for None, so it must live inside the None check.
Line 2The inner else covers the empty string, where name[0] would raise IndexError.
Line 3The outer else covers the missing value and returns a placeholder instead.
Line 4Every path returns, so the function can never fall off the end and give None.
Important notes
Because binding is by indentation, a one-level shift of an else is a legal program with different logic, so mixing tabs and spaces in nested conditionals is especially dangerous.
`else` also attaches to for, while and try, where it means something entirely different (loop finished without break); do not carry the if/else intuition over to those.
Common mistakes
Writing `else n > 0:` or `else if ...` — else takes no condition, so Python raises SyntaxError at that line.
Indenting an else to match the inner if when it was meant for the outer one: the program still runs but the branch fires in the wrong situation, with no error to point at it.
Assigning a variable only inside nested branches and reading it afterwards; when the outer if passes but no inner branch matches, the name was never bound and you get UnboundLocalError or NameError.
Try it yourself
Change, predict, then run
Write a function report(temp, raining) that uses one outer if/else on raining and a nested if/else on temp > 20 to print one of four distinct messages. Call it with all four combinations and confirm each message appears exactly once.
Open the Python workspaceCheck your understanding
With x = 3 and y = 0, what does this print? if x > 0: if y > 0: print("A") else: print("B") print("C")
- A then C
- B then C
- only C
- only B
Show answer
The else is at the same indentation as `if x > 0`, so it belongs to the outer test. x > 0 is true, so the else is skipped entirely; the inner if is false and has no else, so nothing prints from the nested block, leaving only the unconditional "C". "B then C" is tempting if you read the else as belonging to `if y > 0`, but indentation, not proximity, decides the pairing.