A. Programming Concepts and Skills · Programming Basics: Data, Decisions and LoopsNested and Compound Selection
A2.2 A2.3 A1.4 A4.4About 70 minutes
Learning goals- I can write and trace an
if statement nested inside another if. - I can decide when to nest and when to combine conditions with
and or or. - I can use selection to protect a calculation from bad input, such as dividing by zero.
- I can draw a decision tree and turn it into code.
Words to know
nested selection
An if statement placed inside a block of another if, elif or else.
compound condition
A condition made from smaller ones with and, or and not.
decision tree
A branching diagram of questions and outcomes, used to plan nested selection.
guard
A condition that checks a value is safe before it is used, such as if den != 0: before dividing.
run-time error
An error that happens while the program runs, such as ZeroDivisionError. Lines before it have already run.
Questions inside questions
A movie theatre's prices: children under 13 pay $8, seniors (65 or over) pay $9, and everyone else pays $14, except on Tuesdays, when they pay $10. To find a price, you first ask about age, and only for one age group do you ask about the day. A question asked only after another question is answered is nested selection.
Expectation A2.3 asks for algorithms with nested structures. In this lesson you nest selection inside selection; in Lesson 13 you will nest selection inside loops.
Writing a nested if
An if inside a block is indented one more level (4 more spaces). Its whole if/elif/else belongs to the outer block.
- The outer decision runs first.
- The inner decision runs only when Python enters that outer block.
Indentation shows exactly which else belongs to which if, so line it up carefully.
A decision tree for the movie prices in Example 1. Only the 13-to-64 branch asks a second question, so that is where the nested if goes.Worked exampleExample 1: Movie prices
Write code that sets price from age and a Boolean is_tuesday, using the theatre's rules.
- Draw the decision tree (above). The first question is age, with three branches.
- Only the middle branch (13 to 64) has a second question, so put the nested
if there. - Code:
-
if age < 13:
- price = 8
- elif age >= 65:
- price = 9
- else:
- if is_tuesday:
- price = 10
- else:
- price = 14 - Trace: age 30, Tuesday: not < 13, not ≥ 65, so the
else; inside, is_tuesday is True → $10. - Trace: age 70, Tuesday:
age >= 65 → $9. The inner question is never asked.
Children $8, seniors $9, others $10 on Tuesday and $14 otherwise.
Nest, or combine with and?
The nested else in Example 1 could also be written flat:
elif is_tuesday: → price = 10else: → price = 14
Because the chain only reaches that elif when the age is 13 to 64, it means the same thing. In general:
if A: with if B: nested directly inside (and nothing else) is the same as if A and B:. Prefer the flat version: less indentation, easier to read.- Nest when the outer branch does something of its own, or when the inner question only makes sense, or is only safe, after the outer one is True.
Guards: protecting your program
Some operations crash with certain values. Dividing by zero raises a ZeroDivisionError, a run-time error: the program stops in the middle. An if can guard the dangerous step so it runs only with safe values. This is the most important use of nested selection.
A decision inside another decision. The inner question (is there a remainder?) is only asked when the outer one is True, so the program never divides by zero.num = int(input("Numerator: "))
den = int(input("Denominator: "))
if den != 0:
if num % den == 0:
print(f"{num} / {den} divides evenly: {num // den}")
else:
print(f"{num} / {den} has a remainder of {num % den}")
else:
print("Cannot divide by 0.")
With a denominator of 0, the guard sends the program to the outer else and it prints Cannot divide by 0. instead of crashing. Output, line by line: Numerator: 17 | Denominator: 5 | 17 / 5 has a remainder of 2 (the typed input is shown after each prompt)
Worked exampleExample 2: Trace a nested structure
Trace this code for x = 4, y = -2 and for x = -1, y = 5.
if x > 0: if y > 0: q = "I" else: q = "IV"else: if y > 0: q = "II" else: q = "III"print(q)
- It reports the quadrant of the point (x, y) on a coordinate grid (points on an axis go to the
else branches; a real program would check them first). - x = 4, y = −2: outer
x > 0 is True, so enter the first block. Inner y > 0 is False, so the inner else: q = "IV". - x = −1, y = 5: outer is False, so enter the outer
else. Inner y > 0 is True: q = "II".
IV for (4, −2) and II for (−1, 5).
Just for funIf it is snowing, and if it is a school day, and if the buses are cancelled, and if your homework is done, then you get to sleep in.
Programmers call that a nested if. Everyone else calls it "very unlikely".
Hands-on activityDecision tree for a board game or sport
You need: paper, a pencil, and Python. Choose a rule set you know: who goes first in a board game, which hockey age division a player is in, or how a curling end is scored.
- Draw a decision tree with at least two levels of questions. Label every branch.
- Decide which questions are independent (use
and/or or separate ifs) and which depend on an earlier answer (nest them). - Write the program with
input() for each piece of information. Use a guard for any input that could break a calculation. - Make a test table: one row for every leaf (end point) of your tree, with the inputs and the expected output.
- Run every test. Ask a family member to try to "break" your program with odd inputs.
What to look for: the number of test rows equals the number of leaves in the tree; every else lines up under its own if; there is no nested if that could be replaced by a simple and.
Your turn
1What is printed for a = 5, b = 3? For a = 5, b = 8? For a = -1, b = 8?
if a > 0: if b > a: print("X") else: print("Y")else: print("Z")
2Rewrite without nesting:
if temp < 0: if wind > 30: print("Stay inside")
if temp < 0 and wind > 30: with print("Stay inside") indented under it.
3Write a guard so that average = total / count only runs when count is not 0; otherwise print No data.
Sample:
if count != 0: average = total / count print(average)else: print("No data")
4Using Example 1's rules, what is the price for (a) age 12 on a Tuesday, (b) age 13 on a Friday, (c) age 64 on a Tuesday?
(a) $8 (under 13; the day is never checked), (b) $14, (c) $10.
5Why does if count != 0 and total / count > 50: not crash when count is 0?
Short-circuit evaluation: count != 0 is False, so Python never evaluates the division.
6Write nested selection that reads a mark from 0 to 100. If it is outside that range print Invalid; otherwise print Pass (50 or more) or Not yet.
Sample:
mark = int(input("Mark: "))if 0 <= mark <= 100: if mark >= 50: print("Pass") else: print("Not yet")else: print("Invalid")
More practice: warm-up
1What is nested selection?
An if statement inside a block of another if, elif or else.
2How do you show that an if is inside another if?
Indent it one more level (4 more spaces).
3What error does 5 / 0 cause?
4Name the condition that checks a value before a risky operation.
5if A: with if B: nested directly inside and nothing else: what single condition is the same?
More practice: on your own
1Find the bug: the program should print Freezing rain only when it is precipitating and the temperature is from −3 to 0 (inclusive).
if precip: if -3 <= temp <= 0: print("Freezing rain")else: print("Snow")
The else lines up with if precip:, so Snow prints when there is no precipitation, which is wrong. The else probably belongs to the inner if (and even then, "snow" should depend on the temperature being below −3). Indentation decides which if an else belongs to.
2A curling club: members pay $25 per game; non-members pay $35, or $30 if they are students. Write the selection with variables member and student (both Booleans).
Sample:
if member: fee = 25else: if student: fee = 30 else: fee = 35
(Flat version: if member: / elif student: / else:.)
3Trace for n = 12:
if n % 2 == 0: if n % 3 == 0: print("six") else: print("two")else: print("odd")
12 is even, so enter the block; 12 % 3 is 0, so it prints six.
4How many test cases are needed to make every print in the previous question run at least once? Give one value for each.
Three: a multiple of 6 such as 12 (six), an even number not divisible by 3 such as 4 (two), and an odd number such as 7 (odd).
5Write a program that reads two numbers and an operator (+, -, * or /) and prints the result, guarding against division by zero and unknown operators.
Sample:
a = float(input("a: "))op = input("operator: ")b = float(input("b: "))if op == "+": print(a + b)elif op == "-": print(a - b)elif op == "*": print(a * b)elif op == "/": if b != 0: print(a / b) else: print("Cannot divide by 0")else: print("Unknown operator")
The division branch has a nested guard.
Challenge
1Write a program that reads the coefficients of a linear equation ax + b = 0 and prints the solution, handling a = 0 correctly (no solution, or every x is a solution).
Sample:
a = float(input("a: "))b = float(input("b: "))if a != 0: print("x =", -b / a)else: if b == 0: print("Every x is a solution") else: print("No solution")
If a = 0 the equation is just b = 0: always true when b = 0, never true otherwise.
2Rock, paper, scissors: write a condition (one expression with and/or) that is True when player 1 wins, using strings p1 and p2.
(p1 == "rock" and p2 == "scissors") or (p1 == "scissors" and p2 == "paper") or (p1 == "paper" and p2 == "rock"). A full program checks p1 == p2 first (a tie), then this condition, else player 2 wins.
3Explain why this structure can never print C, and suggest what the author probably meant:
if x > 10: print("A")elif x > 20: print("B") if x < 5: print("C")
The elif x > 20 branch is unreachable, because any x > 20 is also > 10 and is caught by the first branch. And even inside it, x < 5 can never be true when x > 20. The author probably meant to check x > 20 first (then x > 10), and to put the x < 5 test in a separate elif or else branch.
Check your understandingQuiz
1. Which is the same as if a: with if b: nested directly inside it (and nothing else)?
2. For x = 3: if x > 0: → (if x > 5: → print("big"), else: → print("small")), else: → print("negative"). What is printed?
4. What decides which if an else belongs to in Python?
5. A decision tree has 5 leaves (end points). What is the minimum number of tests to run every branch?
6. Which is the best reason to nest instead of using and?
For parents and teachersBig idea: expectation A2.2 (selection) extended to nested structures (A2.3), using compound Boolean conditions (A1.4) and tracing (A4.4).
How to help: ask your teen to explain a decision tree for something familiar (a sports league's age divisions, a bus fare chart) and count its leaves. Then ask how many tests are needed.
Common difficulties:
- An
else at the wrong indentation, so it belongs to the wrong if. - Deep nesting where a simple
and would do. - Forgetting a guard, so the program crashes on 0 or on an empty value.
- Branches that can never be reached because an earlier condition already caught those values.