Chapter 06 of 24 | Python
Decisions and loops
Control flow lets a program choose actions and repeat work. A condition is an expression tested for truthiness. Use if, elif, and else to describe mutually exclusive paths.
temperature = 18
if temperature >= 25:
advice = "Stay cool"
elif temperature >= 15:
advice = "A light jacket may help"
else:
advice = "Wear a warm coat"
print(advice)
Only the first matching branch runs. Order conditions from the most specific to the more general when one condition includes another. A missing or misplaced indentation level changes which statements belong to a branch.
For loops and iteration
for number in range(1, 6):
print(number)
names = ["Mina", "Leo", "Sam"]
for index, name in enumerate(names, start=1):
print(index, name)
A Python for loop iterates over items from an iterable; it is not limited to numeric counters. range(start, stop) includes the start and excludes the stop. Use enumerate when both a position and an item are needed, instead of manually maintaining a counter.
While loops and termination
attempts = 0
while attempts < 3:
answer = input("Type yes: ")
if answer == "yes":
break
attempts += 1
else:
print("No successful attempt")
A while loop continues while its condition is true. Ensure that some path changes the state or exits; otherwise the loop can run forever. The optional loop else runs only if the loop ends normally, not when break exits it.
Comprehensions and lazy iteration
squares = [number * number for number in range(10)]
even_squares = [square for square in squares if square % 2 == 0]
square_stream = (number * number for number in range(10))
A list comprehension constructs a list immediately. A generator expression produces values lazily as they are requested, which can save memory for large sequences. Prefer a regular loop when it makes multi-step logic easier to read.
Beginner-to-master progression
- Beginner: make choices and repeat a known or condition-based task.
- Practitioner: iterate directly over collections and test empty, one-item, and boundary cases.
- Advanced: reason about loop invariants, termination, generator backpressure, and the memory cost of eager versus lazy pipelines.
Exercises
- Print the multiples of seven between 1 and 100.
- Read values until the user enters a blank line, then report the count and total.
- Convert a list-building loop into a generator and compare memory use on a large range.
- State an invariant that explains why a loop correctly computes a running sum.
Nested loops and combinatorial growth
A nested loop often performs work proportional to the product of its iteration counts. Comparing every pair in a list may take roughly n squared operations. When n doubles, the work may grow by about four times. Some nested loops are still linear if an inner index only moves forward across the whole input, so analyze actual total iterations rather than counting indentation levels mechanically.
values = [2, 4, 6, 8]
for left_index, left in enumerate(values):
for right in values[left_index + 1:]:
print(left, right)
The slice in this example creates a new list on every outer iteration. That is clear for a small example but adds copying for large sequences. An index-based inner loop avoids that allocation; a more specialized iterator approach may express the pair generation clearly. Profile before optimizing, and preserve the correctness conditions.
State machines and exhaustive cases
For a process with named states, represent the state explicitly rather than scattering loosely related Boolean flags. A transition table can list the current state, event, next state, and action. This makes invalid transitions visible and supports systematic tests.
transitions = {
("closed", "open"): "opened",
("opened", "close"): "closed",
}
state = transitions.get((state, event), state)
For complex systems, silently retaining the old state on an invalid transition may hide a bug. Choose whether to reject the event, return an error, or record it. Exhaustively test every state/event pair that the system claims to support.
Mastery checkpoint
- Estimate the work of a nested loop for increasing input sizes and compare it with measured results.
- State an invariant and a decreasing termination measure for a loop that removes items from a work queue.
- Model a small workflow as a state machine and test all valid and invalid transitions.