Today's outcomes
- Trace if, elif, and else in order.
- Separate validation from classification.
- Test threshold and no-data cases.
Before you start: Complete Day 04 and its practical challenge.
Understand the concept
A branch chooses which statements run
An if statement evaluates a condition. If it is true, Python runs the indented block. An optional else block handles the alternative. In an if/elif/else chain, Python uses the first true branch and skips the remaining branches. The order is part of the program's meaning whenever conditions overlap.
For a score band, “at least 80” is also “at least 60.” Checking the 60 condition first would classify 90 into the lower band. One solution is to check higher thresholds first. Another is to use nonoverlapping ranges. Choose one clear structure and test its boundaries instead of mixing several partly overlapping rules.
The colon begins a suite, and indentation identifies its statements. Use consistent four-space indentation. Two statements aligned at the same indentation level belong to the same block. A line moved out of that block can run for every input rather than only the intended case, even if the resulting program remains syntactically valid.
Validation and classification answer different questions
Validation asks whether an input belongs to the accepted domain. Classification asks what outcome applies to a valid input. A score of -5 should not be described as an ordinary low score when the domain is 0 through 100. Check validity before assigning a performance band.
Likewise, a missing score is not a score of zero. In the example below, None means no observation is available. Handle that case before numeric comparisons, because comparing None with an integer using < is not a meaningful score comparison and raises TypeError.
Today, the accepted types are None or an integer. That assumption avoids introducing full type validation before the exception lesson. If an external source can supply arbitrary strings, lists, or Booleans, a later boundary function must validate those types explicitly.
Several if statements are not one exclusive chain
Two separate if statements can both run. That is useful when several independent warnings may apply. It is different from one if/elif chain that selects exactly one outcome. If your output contract promises one label, a single exclusive chain is usually easier to trace.
Nested conditions can represent decisions that depend on earlier choices. For example, after confirming a learner has enough completed days, you may inspect their practice duration. Deep nesting is not a badge of sophistication. When several conditions can be combined or an invalid case can be handled first, prefer the structure that makes the written rule easiest to inspect.
Worked example
score = 80 # Accepted input: None or an integer.
if score is None:
label = "not recorded"
elif score < 0 or score > 100:
label = "invalid"
elif score >= 80:
label = "distinction"
elif score >= 60:
label = "pass"
else:
label = "review"
print("Score:", score)
print("Outcome:", label)
| Input | First applicable decision | Outcome |
|---|---|---|
| None | Missing observation | not recorded |
| -1 | Outside accepted range | invalid |
| 59 | No higher threshold met | review |
| 60 | At least 60 | pass |
| 79 | At least 60, below 80 | pass |
| 80 | At least 80 | distinction |
| 101 | Outside accepted range | invalid |
The table is a specification you can compare with the implementation. It also shows why validation precedes the high-score threshold: 101 is at least 80 but must not receive distinction under this contract.
Guided practice
- Predict: trace the worked example for 60 and 101. Record every condition checked before the selected branch. Skipped branches do not perform their statements.
- Modify: introduce a new top band “excellent” for 95 through 100. Add tests for 94, 95, and 100, while keeping 101 invalid. Explain where the new condition belongs.
- Reconstruct: write a single chain for a nonnegative minute count: zero is “not started,” 1–59 is “short session,” and 60 or more is “full session.” Handle a negative count as invalid before classifying it.
Use the exact verbal boundaries in your tests. “At least,” “more than,” “up to,” and “below” are not interchangeable. Before writing a condition, translate the boundary into one included example and one excluded example.
Daily challenge
Build a study-plan status checker from completed_days and planned_days. Both inputs are integers. Planned days must be positive, and completed days must be between zero and planned_days. For valid input, classify zero completed days as “not started,” all planned days as “complete,” and any value between them as “in progress.” Print exactly one status.
| Planned | Completed | Expected status |
|---|---|---|
| 30 | 0 | not started |
| 30 | 12 | in progress |
| 30 | 30 | complete |
| 30 | 31 | invalid |
| 0 | 0 | invalid |
| 30 | -1 | invalid |
Keep validation separate from progress interpretation. A completion count above the plan is not an exceptional achievement under this contract; it is an invalid state requiring investigation. A different application could permit bonus work, but it would need a different stated rule.
Reference solution
Compare after attempting the challenge
planned_days = 30
completed_days = 12
if planned_days <= 0 or completed_days < 0 or completed_days > planned_days:
status = "invalid"
elif completed_days == 0:
status = "not started"
elif completed_days == planned_days:
status = "complete"
else:
status = "in progress"
print(status)
The else branch is safe here because the earlier checks establish the remaining valid domain. It does not mean “everything else in the world is a valid in-progress plan.” The accepted integer-type assumption still belongs to the contract.
Debug and explain
| Mistake | Observable effect | Diagnostic case |
|---|---|---|
| Completion checked before validity | Zero-length plan may appear complete | planned 0, completed 0 |
> used for an inclusive threshold |
Exact boundary is misclassified | score 60 |
| Independent if statements used for exclusive labels | Several labels may print | score 90 |
| Missing input compared numerically first | TypeError before a useful outcome | score None |
Explain why branch order matters even when every individual condition looks reasonable. Give a case that reaches the final else. Then explain the assumptions that make that case valid. This habit prevents an else branch from becoming a catch-all that hides bad data.
For each repaired bug, keep the input that exposed it. That input becomes a regression case when the rules change. Do not discard it merely because the program now produces the expected answer.
Review and next step
Save the six challenge outputs and one branch trace. Tomorrow, rebuild the checker without copying before adding repetition. Day 6 will apply decisions across several observations and introduce controlled loops.
Sources and verification
Original teaching examples checked with Python 3.12.14. Library behavior reviewed against these primary references on October 3, 2026. Day 26's examples also use Beautiful Soup 4.14.3.
Check your understanding
Five questions, 20 points each. Pass at 85% or above, which requires all five correct. Feedback explains each answer; revise the lesson and retry when needed.
Review your practical work
Check these statements after running your own challenge. This is your self-review; the website does not execute or assess your Python code.
Pass the knowledge check and finish the practical self-review to save completion.