NOETRION

Choose a topic

DAY 05 OF 30 · RUN AND REASON

Conditional statements

Translate written rules into mutually understandable branches and test their boundaries.

90–120 minutes suggested study timePython 3.12Not yet completed

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

  1. Predict: trace the worked example for 60 and 101. Record every condition checked before the selected branch. Skipped branches do not perform their statements.
  2. 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.
  3. 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.

DAILY CHECKPOINT

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.

01. In an if/elif chain, how many matching branches run?

02. Why is if minutes insufficient for a missing-value test?

03. How do you test a range from 0 through 240 inclusively?

04. What does else represent in a conditional chain?

05. Why test values directly at a threshold?

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.