What you'll learn
A useful script rarely evaluates a single variable once. It makes decisions, processes multiple records, and knows when to stop. This lesson builds those skills with small, readable examples before introducing a real coding exercise.
- Explain how Python evaluates if / elif / else in order and selects one branch.
- Predict how for loops iterate over ranges and small collections, including exclusive stop values.
- Use counters and accumulators to summarize repeated checks.
- Write bounded while loops with clear exit conditions.
- Distinguish break from continue and trace their effect on execution.
- Debug indentation, off-by-one errors, and loops that fail to update state.
- Predict nested-loop iteration counts and safely filter site/service combinations using two loops.
From one if to several decisions
Sometimes a device is neither simply healthy nor unhealthy. Python's if, elif and else chain examines conditions from top to bottom. It executes the first matching branch and skips the rest, even when a later comparison could also be true. Order the most specific conditions intentionally.
One branch wins
An elif is tested only when earlier if/elif conditions were false. else runs only if none of the previous conditions matched; it has no condition of its own.
Threshold order matters
For a priority scale, test latency >= 200 before latency >= 100. If the broad check comes first, a value of 240 may be incorrectly labeled 'warning' instead of 'critical'.
latency = 240
if latency >= 200:
print("Critical")
elif latency >= 100:
print("Warning")
else:
print("Normal")What would print if latency were 140? What if it were exactly 200? Reveal explanation after you predict.
140 prints Warning. 200 prints Critical because the first comparison includes the boundary.
A for loop visits each item
When a technician needs to inspect several records, writing the same test five times is error-prone. A for loop binds a variable to each item in an iterable and executes its indented block once for each item. For now, a short list represents an inventory; we'll explore data structures deeply in a later lesson.
Loop variable
In for name in devices:, name takes the next item from devices. The original collection isn't automatically modified by visiting it.
Indentation defines repetition
print(name) inside the loop runs for every item. Put print('Done') outside the loop to run once after the entire iteration finishes.
devices = ["SW-01", "FW-01", "AP-02"]
for name in devices:
print(name)
print("Inventory reviewed")How many times does the final line print? Why? Reveal explanation after you predict.
Inventory reviewed prints once because its statement is outside the indented loop body.
Understand range before writing counters
range(start, stop, step) produces a sequence of integer values. Its stop value is not included, so range(1, 4) visits 1, 2 and 3. If you forget the exclusive boundary, your final port or retry count may silently be missed.
One argument
range(4) visits 0, 1, 2, 3. Zero-based counting is common, but make sure it matches the task's requirements.
Three arguments
range(2, 9, 3) visits 2, 5, 8. A positive step must move toward the upper bound, while a negative step can count downward.
for attempt in range(1, 4):
print(f"Attempt {attempt}")How many attempts are printed, and what would range(1, 5) do? Reveal explanation after you predict.
Three attempts are printed: 1, 2 and 3. range(1, 5) adds a fourth attempt.
See every iteration.
Predict what the loop will do, change the conditions, then step through its decisions. The editable workbench later in this lesson runs actual Python.
for attempt in range(1, 4):
print(f"Attempt {attempt}")Predict the result before executing the first iteration.
(nothing yet)
Educational trace of three fixed examples; Python execution and variable tracing are available in the full workbench.
Count a result without losing state
Sometimes you want a single report from many records. An accumulator is a variable initialized before the loop and updated inside it. Resetting a counter inside the loop discards previous results, and printing inside the loop may produce intermediate values rather than the requested final summary.
Initialize before the loop
Start offline_count = 0 before visiting devices. Each offline record increases it by one. This counts matches rather than the total inventory size.
Separate process from report
A final print statement is often outside the loop. If it's accidentally indented, a report prints repeatedly before the scan finishes.
statuses = ["online", "offline", "offline", "online"]
offline_count = 0
for state in statuses:
if state == "offline":
offline_count += 1
print(f"{offline_count} offline devices")What would go wrong if offline_count = 0 were placed immediately under for? Reveal explanation after you predict.
It would reset the count every iteration, so the final result would reflect only the most recent item instead of all offline records.
A while loop needs a way out
A for loop is ideal for a known sequence. A while loop repeats for as long as a condition remains true, which can model retries or waiting for a result. Unlike a for range, your code usually must update the condition's state. Missing that update causes an endless loop.
Check before every iteration
while attempt <= 3: evaluates its Boolean condition before the body. Once attempt becomes 4, the loop stops normally.
Avoid unbounded behavior
while True: requires a deliberate break or other exit. In the beginner workbench, use explicit counters and avoid real network retries.
attempt = 1
while attempt <= 3:
print(f"Checking attempt {attempt}")
attempt += 1
print("Finished")What does attempt contain when the loop finishes? Reveal explanation after you predict.
4. The last completed body increment moves attempt from 3 to 4, so the next condition fails.
continue is not break
When scanning event codes, some entries may be irrelevant while others require stopping the investigation. continue skips the rest of the current iteration and moves to the next item; break exits the nearest enclosing loop. Confusing them can skip an important record or stop a report too early.
Skip a harmless record
An HTTP 200 can be skipped if you're building a report of unusual responses. continue returns to the next loop iteration, not the line after the entire loop.
Stop on a critical response
If the workflow needs the first server error (HTTP 500+), break prevents examining later entries. Whether stopping early is appropriate depends on the task.
responses = [200, 429, 503, 200]
for code in responses:
if code == 200:
continue
if code >= 500:
print(f"Critical: {code}")
break
print(f"Review: {code}")Which responses are printed before the loop stops? Reveal explanation after you predict.
Review: 429 and then Critical: 503. The 200 is skipped; break prevents examining the last item.
Read nested logic one level at a time
A nested loop means a complete inner loop runs once for every outer-loop item. For two sites and three services, Python visits six site–service combinations in a predictable order. Trace the outer item, then walk the full inner sequence before the outer loop advances. An if statement inside a loop is different: it makes a decision but does not create additional iterations.
Outer and inner responsibilities
for site in sites: chooses a site. The indented for service in services: checks each service at that one site. When services are exhausted, the outer loop selects the next site.
Count the iterations
If sites contains 2 items and services contains 3, the inner body executes 2 × 3 = 6 times. An empty inner list yields zero visits regardless of how many outer items exist.
Indentation reveals structure
A report printed inside the inner loop runs for every pair. A report after the inner loop, but still inside the outer loop, runs once per site. The variable names and indentation tell you which is intended.
sites = ['HQ', 'BR1']
services = ['dns', 'dhcp', 'vpn']
checks = 0
for site in sites:
for service in services:
checks += 1
print(f'{site}: {service}')
print(f'{checks} total checks')List the first three lines. Which site appears when the fourth service check starts, and how many checks print at the end? Reveal explanation after you predict.
The first lines are HQ: dns, HQ: dhcp, and HQ: vpn. The fourth visit starts BR1: dns because the inner loop finishes for HQ before the outer site advances. The last line is 6 total checks.
Trace a grid and filter combinations
For a two-dimensional grid, a schedule, or a small site-by-service checklist, two loops describe two coordinates. Each outer item is paired with each inner item in order. To filter the combinations, put an if statement inside the inner loop. A useful debugging habit is to record the pair before deciding whether it matches: this shows whether a bug comes from the loop boundary, the condition, or the output. Avoid accidentally resetting the result list inside the outer loop.
Pairwise traversal
The row/column combinations in a 2 × 3 grid are (0,0), (0,1), (0,2), (1,0), (1,1), (1,2). range(2) yields only 0 and 1; range(3) yields 0, 1 and 2.
Filter the pairs
When a particular site and service are in a fictional failures set, report the matching pair; otherwise continue. A set is convenient for membership, but simple lists work too.
Empty collections and loop placement
An empty rows or columns sequence means no combinations. Initialize the count or result once before both loops, then produce the final report only after both loops finish.
sites = ['HQ', 'BR1']
services = ['dns', 'vpn']
failed = {'HQ:vpn', 'BR1:dns'}
matches = []
for site in sites:
for service in services:
pair = f'{site}:{service}'
if pair in failed:
matches.append(pair)
print(sorted(matches))What list prints? Why doesn't the algorithm report HQ:dns or BR1:vpn, and what if services is empty? Reveal explanation after you predict.
It prints ['BR1:dns', 'HQ:vpn'] after sorting. The other two pairs are not in the failures set, so they are skipped. If services is empty, the inner loop never runs and matches stays empty.
One outer item. Every inner item.
Step through a fictional site-by-service grid. The inner loop visits every service at the selected site before the outer loop advances.
Visit 1 of 6: outer site = HQ; inner service = dns
for site in sites:
for service in services:
print(site, service)Debug the loop, not just the output
A loop can be valid Python and still be wrong. Typical problems include an off-by-one range, a misplaced counter initialization, a print statement at the wrong indentation, a condition that never changes, or using break where continue was intended. Start from the observed behavior and compare each iteration to the requirement.
Follow variable snapshots
The Python workbench displays state before each executed line and the final state. Use it to see whether a counter changes after an if, and whether a while condition eventually becomes false.
Test more than one sample
After the initial exercise succeeds, edit the data and check boundaries. Does your script still work for zero matches, one match and several matches? Real automation must survive unexpected inputs.
ports = [22, 80, 443, 3389]
review_count = 0
for port in ports:
if port == 22 or port == 443:
continue
review_count += 1
print(f"{review_count} ports need review")Which values contribute to review_count? Reveal explanation after you predict.
80 and 3389. The 22 and 443 entries trigger continue and are not counted.
Turn control flow into working code
The first challenge asks you to fix overlapping latency thresholds using if/elif/else. Run real Python, predict the output, inspect the trace, then continue into loops, bounds, nested-loop grids and a simulated port audit in Practice.
Write it. Run it. Understand it.
Write decisions and loops, inspect each iteration, and use feedback to verify the exact result.
Classify latency without overlapping rules
Northstar classifies latency at 200 ms or higher as Critical, at 100–199 as Warning, and below 100 as Normal. The current test is 240 ms. Fix the decision order so the exact output is 'Critical'; keep the result in the variable severity.
CriticalTry solving it before asking for help. Hints never show a completed program.
Your printed output and errors will appear here.
Run the code, then step through the executed lines to watch variable values change.
Practice is where you become independent.
Seven further challenges build from for/range and bounded while loops to nested-grid traversal, pair matching and an IT-flavored inventory audit. Each asks you to write or correct the logic, rather than only identify the right answer.
Continue to all Control Flow challenges →Reference material
You don't need to memorize everything. Learn how to read the Python documentation and use it when a behavior is unfamiliar.
