Exercises — Nested conditionals
This page is a companion to Nested Conditionals. If any word here feels unfamiliar, the linked notes at the bottom build it from scratch.
How to read a nested if by hand
Before the ladder, one tool you will use in every solution: the indentation ruler.

L1 — Recognition
Exercise 1.1 (L1)
Which of these snippets contains a nested conditional (an if/elif/else inside the body of another)?
# (A)
if a > 0:
print("pos")
elif a < 0:
print("neg")
# (B)
if a > 0:
if b > 0:
print("both pos")
# (C)
if a > 0 and b > 0:
print("both pos")Recall Solution 1.1
Answer: (B).
- (A) is
if/elif— these are siblings at the same indentation lane, not one inside the other. No nesting. - (B) has
if b > 0:sitting indented inside the body ofif a > 0:. That innerifonly runs when the outer isTrue. This is the nest. - (C) uses
andto combine two conditions on one line. It behaves like (B) for a single outcome, but structurally it is a flat singleif— no innerif. This is the "flatten withand" pattern from the parent note.
Exercise 1.2 (L1)
In the code below, which if does the else belong to?
if temp > 30:
if humid > 70:
print("muggy")
else:
print("hot and dry")Recall Solution 1.2
The else sits in the same lane (one indent deep) as if humid > 70:. So it pairs with if humid > 70. It is the inner else. Meaning: we only reach it after temp > 30 was already True, and then found humid > 70 was False → prints hot and dry.
L2 — Application
Exercise 2.1 (L2)
Predict the output.
score = 85
if score >= 60:
if score >= 90:
print("A")
else:
print("Pass")
else:
print("Fail")Recall Solution 2.1
score >= 60?85 >= 60→True. Enter outer body; outerelseis skipped.score >= 90?85 >= 90→False→ take innerelse.- Output:
Pass
Exercise 2.2 (L2)
Predict the output for each value of n: n = 0, n = 7, n = -3.
if n >= 0:
if n == 0:
print("zero")
else:
print("positive")
else:
print("negative")Recall Solution 2.2
Walk all three — covering every case the code can hit:
n = 0:0 >= 0True →0 == 0True →zero.n = 7:7 >= 0True →7 == 0False → innerelse→positive.n = -3:-3 >= 0False → outerelse→negative.
Notice: the outer gate splits sign; the inner gate only distinguishes zero-vs-positive inside the non-negative branch. The negative branch never asks the == 0 question — it can't be zero there.
Exercise 2.3 (L2)
Predict the output.
logged_in = True
is_admin = True
banned = True
if logged_in:
if banned:
print("Access denied")
else:
if is_admin:
print("Admin panel")
else:
print("User home")
else:
print("Login required")Recall Solution 2.3
logged_inTrue → enter.bannedTrue →Access denied, and we stop. Theis_adminquestion is never asked — banned overrides everything inside this branch.- Output:
Access denied
L3 — Analysis (find & fix the bug)
Exercise 3.1 (L3)
The intended behaviour: print both positive only when both are positive; otherwise print a not positive only when a <= 0. This code is wrong. Explain why and fix it.
if a > 0:
if b > 0:
print("both positive")
else:
print("a not positive")Recall Solution 3.1
This code is actually correct as written — the else sits in the same lane as if a > 0, so it pairs with the outer if. Trace it:
a > 0False → outerelse→a not positive. ✅a > 0True,b > 0True →both positive. ✅a > 0True,b > 0False → nothing prints (the innerifhas noelse), which matches the spec ("only when a<=0" for the message). ✅
The lesson: the lane test confirms correctness. The else is one lane out, so it is the outer else. Don't be spooked by how close the else looks to if b > 0 — measure the lane, not the eyeball distance.
Exercise 3.2 (L3)
This is meant to print both positive for a=1, b=-1... no wait — read the spec: it should print inner else when a > 0 but b <= 0. It doesn't. Fix it so the else attaches to the inner if.
if a > 0:
if b > 0:
print("both positive")
else:
print("inner else")Recall Solution 3.2
As written, else is on the outer lane, so it fires when a <= 0 — that is the dangling-else trap. To make else pair with if b > 0, push it one lane deeper so it sits in the inner body's lane:
if a > 0:
if b > 0:
print("both positive")
else:
print("inner else")Now with a=1, b=-1: a>0 True → b>0 False → inner else. And with a=-5: a>0 False → nothing prints (there is no outer else anymore). Compare the two lane pictures below.

Exercise 3.3 (L3)
Find the runtime/syntax problem and fix it.
x = 5
if x = 5:
print("five")Recall Solution 3.3
if x = 5: raises a SyntaxError. = is the assignment operator ("put 5 into x"); a condition needs a comparison. Fix:
if x == 5:
print("five")Now x == 5 asks "is x equal to 5?" → True → prints five. See Comparison operators.
L4 — Synthesis (build it from words)
Exercise 4.1 (L4)
Write nested conditionals for an ATM:
- If
card_validisFalse: printCard rejected. - If the card is valid but
pin_okisFalse: printWrong PIN. - If both are fine but
amount > balance: printInsufficient funds. - Otherwise: print
Dispensing cash.
Recall Solution 4.1
Each check only makes sense after the previous passed — a textbook chain of gates:
if card_valid:
if pin_ok:
if amount > balance:
print("Insufficient funds")
else:
print("Dispensing cash")
else:
print("Wrong PIN")
else:
print("Card rejected")Trace check with card_valid=True, pin_ok=True, amount=100, balance=500:
valid → pin ok → 100 > 500? False → else → Dispensing cash. ✅
Trace with card_valid=True, pin_ok=False: valid → pin False → Wrong PIN (the amount check is never reached — you can't withdraw with a bad PIN). ✅
Exercise 4.2 (L4)
A number n is given. Print one of four labels:
"neg even", "neg odd", "non-neg even", "non-neg odd".
Use an outer gate on sign and an inner gate on parity. (Parity: n % 2 == 0 means even.)
Recall Solution 4.2
% is the remainder operator: n % 2 is what's left after dividing by 2, so it's 0 for even and 1 (or -1) for odd. We test == 0.
if n < 0:
if n % 2 == 0:
print("neg even")
else:
print("neg odd")
else:
if n % 2 == 0:
print("non-neg even")
else:
print("non-neg odd")Cover all four buckets with n = -4, -3, 0, 7:
-4:<0True,-4 % 2 == 0True →neg even.-3:<0True,-3 % 2 == 0? In Python-3 % 2 == 1, so False →neg odd.0:<0False,0 % 2 == 0True →non-neg even.7:<0False,7 % 2 == 0False →non-neg odd. ✅
(Python's % always returns a result with the sign of the divisor 2, so -3 % 2 is 1, not -1. Testing == 0 for evenness is safe in every case.)
L5 — Mastery
Exercise 5.1 (L5) — Prove two versions are equal
Show that this nested version and this flat version produce the same output for every input:
# Nested
if age >= 18:
if has_license:
print("Can drive")
# Flat
if age >= 18 and has_license:
print("Can drive")Recall Solution 5.1
There is exactly one printed outcome (Can drive) and it appears only when both conditions hold. To prove two branchings behave identically we use a truth table: a grid that lists every possible combination of the input conditions and records what each version does. Here each condition is either T (True) or F (False), so with two conditions there are 2 × 2 = 4 rows — every situation the world can present.
Read the table like this: pick a row (a scenario), then follow across to see whether the nested code prints and whether the flat code prints. If those two answers agree in every row, the versions are provably equivalent.
age>=18 |
has_license |
Nested prints? | and value |
Flat prints? |
|---|---|---|---|---|
| T | T | reaches inner, True → yes | T | yes |
| T | F | reaches inner, False → no | F | no |
| F | T | outer skipped → no | F | no |
| F | F | outer skipped → no | F | no |
The "prints?" columns are identical in all four rows, so the versions are equivalent. Because we produce only one message, flattening with and is the readable choice (see Code readability and refactoring and Boolean logic and operators). Keep nesting only when each level needs its own distinct branch/message.
Exercise 5.2 (L5) — Refactor arrow code
Refactor this deep "arrow code" into shallower logic without changing behaviour. Assume every branch either prints or does nothing.
if a:
if b:
if c:
print("all three")Recall Solution 5.2
Three nested gates with a single outcome and no else branches → flatten with and:
if a and b and c:
print("all three")What "short-circuits" means: short-circuit evaluation is when a Boolean operator stops as soon as the answer is certain, without looking at the remaining operands. For and, Python checks the left operand first; if it is falsy the whole and is already False, so Python returns immediately and never even evaluates what comes after. (Likewise or stops at the first truthy operand — see Boolean logic and operators.)
So a and b and c checks a first; if a is falsy it never evaluates b or c. That means the "don't ask the inner question unless the outer passed" safety of nesting is preserved — and gives the same laziness. Depth drops from 3 to 1. Only keep the nest if, say, you needed to print("a failed") at a level — then a branch message forces nesting.
Exercise 5.3 (L5) — Full edge-case sweep
Classify a value x (which may be an int or None) with this spec and write the code, then trace every case:
- If
xisNone:"missing". - Else if
x == 0:"zero". - Else if
x > 0:"positive". - Else:
"negative". Use an outerNone-gate and a nested numeric decision so we never compareNonewith>.
Recall Solution 5.3
Comparing None > 0 raises a TypeError in Python 3, so we must gate None out first — this is the "unsafe inner check" reason for nesting from the parent note:
if x is None:
print("missing")
else:
if x == 0:
print("zero")
elif x > 0:
print("positive")
else:
print("negative")Sweep every case:
x = None:x is NoneTrue →missing(numeric block never runs — safe!).x = 0: not None →x == 0True →zero.x = 42: not None → not 0 →42 > 0True →positive.x = -8: not None → not 0 →-8 > 0False →else→negative.
The outer gate protects the inner comparisons. No input can reach x > 0 while x is None. Every case is covered and none crashes.
Recap ladder
Recall
The five levels ::: L1 recognise a nest vs an elif chain; L2 trace outputs gate-by-gate over all cases; L3 fix dangling-else via the lane rule; L4 build nested logic from a spec; L5 prove equivalence, refactor arrow code, and gate unsafe operations.
The single rule for else pairing ::: it binds to the nearest unmatched if in the same indentation lane.
When to flatten with and ::: one single outcome and no per-level messages, and short-circuiting keeps inner checks safe.
When to keep the nest ::: distinct messages per level, or an inner check that is unsafe unless the outer passed.
Connections
- Nested Conditionals — the parent topic these exercises drill.
- Conditional statements (if-elif-else) — the branch building blocks.
- Boolean logic and operators —
and/short-circuiting used in L5. - Indentation in Python — the lane rule behind every L3 fix.
- Comparison operators —
==,>,<,%used throughout. - Code readability and refactoring — refactoring arrow code in L5.