Unit 2 · Data & Control Flow

Lesson · Unit 2 · 8 min read

break, continue, and nesting, for when a plain loop isn't quite enough.

Three loop tools you'll reach for constantly: break to bail out early, continue to skip an iteration, and nested loops for grids and pairs. Plus the patterns that keep nested loops from turning into spaghetti.

Section · 01

break — exit the loop immediately

break jumps out of the nearest enclosing loop. The rest of the body is skipped, the loop ends, and execution continues on the next line after the loop.

users = ["ada", "ben", "cal", "dee", "ed"]
target = "cal"

for name in users:
    if name == target:
        print(f"Found {name}.")
        break
    print(f"Checked {name}, not it.")

print("Done searching.")

Output:

Checked ada, not it.
Checked ben, not it.
Found cal.
Done searching.

Use breakwhen you have a clear “I’m done” condition mid-loop — finding a value, hitting a sentinel, short-circuiting a search. Don’t use it to build complicated flow control; if you find yourself with two or three breaks in one loop, refactor.

Section · 02

continue — skip the rest of this iteration

continuesays “skip the rest of this pass and jump straight to the next item.” The loop keeps running:

measurements = [12.4, -1.0, 8.7, -0.5, 15.2, 9.0]

# Sum only the positive values
total = 0
for m in measurements:
    if m < 0:
        continue          # skip negatives entirely
    total = total + m

print(total)              # 45.3

You could write the same loop with an if instead of continue:

for m in measurements:
    if m >= 0:
        total = total + m

When the loop body is short, the if version is fine. When the body is long, continuewith a guard at the top reads better — you don’t have to indent the whole real-work block:

for record in records:
    if not record.is_valid:
        continue
    if record.amount == 0:
        continue
    # ... 30 lines of real work, not indented inside two ifs ...

Same trick as the “guard clause” pattern in functions. Flatten the happy path; bail out early on the cases you don’t care about.

Section · 03

Nested loops

A nested loop is a loop inside another loop. The inner loop runs all the way through for each iteration of the outer:

# Multiplication table — 3 rows × 4 columns
for row in range(1, 4):
    for col in range(1, 5):
        product = row * col
        print(f"{product:3}", end=" ")
    print()       # newline at the end of each row

#  1   2   3   4
#  2   4   6   8
#  3   6   9  12

Nested loops are the natural fit for grids, matrices, and pairing things across two collections:

# Find pairs that sum to 10
numbers = [1, 3, 5, 7, 9]

for i, a in enumerate(numbers):
    for b in numbers[i + 1:]:   # only items AFTER i, no duplicate pairs
        if a + b == 10:
            print(f"{a} + {b}")

# 1 + 9
# 3 + 7

Watch the cost. A loop over N items inside a loop over N items does N² work. Fine when N is 10. Brutal when N is 100,000. For the “find pairs” problem above, a set-based approach is faster than nested loops once your list gets big.

Section · 04

break in nested loops — only exits one level

A break exits the innermost loop only. To exit out of multiple levels you need a flag or a function:

# 5x5 grid — find the first cell with value > 100
grid = [[10, 20, 30, 40, 50],
        [60, 70, 80, 90, 99],
        [101, 1, 2, 3, 4],
        [...],
        [...]]

# Approach 1: a flag variable
found = False
for r, row in enumerate(grid):
    for c, value in enumerate(row):
        if value > 100:
            print(f"Found at ({r}, {c}): {value}")
            found = True
            break
    if found:
        break

Works, but the flag adds noise. The cleaner approach in Python is to pull the nested loop into its own function and use returnreturn exits all loops at once because it exits the whole function:

def find_over(grid, threshold):
    for r, row in enumerate(grid):
        for c, value in enumerate(row):
            if value > threshold:
                return (r, c, value)
    return None

result = find_over(grid, 100)
if result:
    r, c, v = result
    print(f"Found at ({r}, {c}): {v}")

When breakout logic gets tangled, a function plus return almost always reads better than nested flags. That refactor is worth knowing.

Section · 05

The else clause on loops

Python lets you attach an else to a loop. It runs only when the loop finishes without hitting break:

def find_user(users, target):
    for u in users:
        if u == target:
            print(f"{target} is registered.")
            break
    else:
        print(f"{target} not found.")    # only runs if no break

This is genuinely useful for “searched everything, didn’t find it” cases. It’s also one of the least-known features of Python, so use it sparingly — half your coworkers will Google it the first time they see it. If unfamiliarity will hurt the code review more than the elegance helps, a regular flag is fine.

Curriculum source

Lesson content is original to YorkSims. Topic structure aligns with Python for Everybody by Dr. Charles R. Severance (py4e.com), licensed under Creative Commons Attribution 3.0 Unported.