Python / Foundations / Lesson 3
Decisions, loops, and invariants
Express rules as branches and repeated work as loops, then prove that each iteration makes progress and preserves a useful invariant.
On this page
Lesson goals
- Make conditions readable
- Separate policy from traversal
Make conditions readable
An if statement selects a block when its condition is true. elif checks another condition only if earlier ones were false, and else covers the remaining cases. Order overlapping rules from the specific case to the broader fallback. Indentation defines the block; visual alignment is therefore part of the program’s meaning. Before writing code, list representative cases and boundaries in ordinary language. A shipping rule with free delivery at a threshold needs examples just below, at, and above that threshold. This technique reveals whether the requirement means greater than or greater than or equal to.
Iterate over values
A for loop requests successive values from an iterable. Prefer iterating directly over items when an index serves no purpose. range(start, stop, step) represents an arithmetic progression whose stop is excluded. That exclusion allows adjacent ranges to meet without overlapping and makes range(len(items)) cover valid sequence positions. Use enumerate when both a position and its item are needed. A loop can contain a branch without making the control flow mysterious: describe what each iteration consumes and what it contributes. Avoid changing the size of a collection while iterating over it unless the algorithm explicitly supports that behavior.
Accumulate with an invariant
An accumulator summarizes values seen so far. Initialize it before the loop, update it once per relevant item, and read it after the loop. An invariant is a statement true before and after every iteration; here total equals the sum of the nonnegative values already visited. Walking through the empty prefix establishes the starting value zero. Extending that prefix by one item explains the update. This reasoning is more reliable than checking only the final answer for one dataset. The example combines filtering and summation while making the rule and the changing state completely visible.
values = [4, -2, 7, 0]
total = 0
for value in values:
if value >= 0:
total += value
print(total)
11
Negative two contributes nothing; four, seven, and zero preserve the stated invariant and produce eleven. An empty list would leave the initialized zero unchanged.
Termination and early exit
A while loop repeats while a condition remains true. Identify a quantity that moves toward termination and ensure every path updates it. break leaves the nearest loop, while continue starts its next iteration. These tools simplify a search when used sparingly. Python’s loop else runs when the loop ends without break, which can express a search failure; it is unrelated to an inner if. The example searches for the first even number, stops at its discovery, and produces a fallback only when the entire search is exhausted. Test both discovery and exhaustion when reviewing such a pattern.
values = [3, 9, 12, 14]
for value in values:
if value % 2 == 0:
print("Found", value)
break
else:
print("No even value")
Found 12
Twelve is the first matching value, so break prevents both the later fourteen and the loop’s else block from being processed.
Separate policy from traversal
A loop says how values are visited; the condition says which values matter. Keep those responsibilities distinguishable so a changed business rule does not force a rewritten traversal. Nested loops multiply work: for each outer item, the inner loop may inspect every inner item. Count approximate operations on a small example before applying that design to large data. Prefer an explicit readable loop over an expression with hidden side effects. During debugging, record iteration number, current item, and accumulator, then remove unnecessary tracing after the invariant and boundary cases have been verified. A terminating program can still implement the wrong rule.
Common errors and fixes
The threshold value follows the wrong branch
- Why it happens
- The comparison uses > where the requirement includes equality.
- How to fix it
- Translate the boundary rule into >= and verify adjacent cases.
- Prevent it next time
- Write below, equal, and above examples before coding.
A while loop never finishes
- Why it happens
- One execution path leaves its progress variable unchanged.
- How to fix it
- Update progress on every relevant path or use a bounded for loop.
- Prevent it next time
- State a decreasing remaining-work measure during review.
The total contains only the final item
- Why it happens
- The accumulator was reset inside the loop.
- How to fix it
- Move initialization before the loop and retain only the update inside.
- Prevent it next time
- Trace the accumulator after two consecutive iterations.
Check your understanding
1. Sum the odd numbers in range(1, 8). Explain the excluded endpoint.
Show worked solution
Initialize total to zero, iterate through range(1, 8), and add n when n % 2 != 0. The visited values are one through seven, so the sum is 1 + 3 + 5 + 7 = 16. Eight is not visited because stop is excluded.
2. Describe a search that returns a fallback for an empty sequence.
Show worked solution
A for loop with a matching branch and break can use its else for the fallback. With no items the loop immediately exhausts without break, so else executes. With a match it skips else. With only nonmatches it also reaches else after inspecting every item.
Key takeaways
- Use boundary cases to validate branches.
- An invariant explains what a loop has achieved so far.
- Termination requires visible progress on every path.