break / continue / pass

break, continue, and pass give fine-grained control over loop execution — exiting early, skipping to the next iteration, or doing nothing at all as an explicit placeholder.

Beyond a loop's own condition, Python provides three statements to alter or mark control flow explicitly: break exits a loop immediately, continue skips the rest of the current iteration and moves to the next one, and pass does absolutely nothing — it exists purely because Python's syntax requires a block to have a body, even when that body is intentionally empty.

break: Exiting a Loop Immediately

break immediately terminates the nearest enclosing for or while loop, skipping any remaining iterations and any else clause the loop might have. Execution resumes at the first statement after the loop.

numbers = [4, 9, 15, 22, 30]
for n in numbers:
    if n % 2 == 0 and n > 20:
        print(f'Found it: {n}')
        break
    print(f'checked {n}')
print('done')
# checked 4
# checked 9
# checked 15
# Found it: 22
# done

continue: Skipping to the Next Iteration

continue skips the rest of the current iteration's body and jumps straight to the loop's next iteration — for a for loop, that means advancing to the next item; for a while loop, it means re-checking the condition immediately.

In a while loop, be careful that any state update needed for loop progress happens *before* the continue, otherwise continue can accidentally create an infinite loop by skipping the very code that would have advanced the condition.

for n in range(1, 11):
    if n % 2 != 0:
        continue        # skip odd numbers
    print(n)
# 2 4 6 8 10
# Correct: update happens before continue
i = 0
while i < 10:
    i += 1              # progress happens first
    if i % 2 != 0:
        continue
    print(i)             # 2 4 6 8 10

pass: The Explicit No-Op

pass is a statement that does nothing when executed. It exists because Python requires every block (if, for, while, def, class, try, etc.) to have a non-empty body — you cannot leave a colon-block syntactically empty. pass fills that requirement explicitly and legibly.

pass is not the same as None or a comment. A comment is not executable syntax and cannot satisfy Python's requirement for a block body — only an actual statement like pass can.

def not_implemented_yet():
    pass   # placeholder -- function body TODO

for item in items:
    if item.is_valid():
        pass   # intentionally do nothing for valid items in this pass
    else:
        handle_invalid(item)

class CustomError(Exception):
    pass   # inherits everything from Exception, adds nothing new

break and continue Inside Nested Loops

Both break and continue apply only to the *nearest enclosing loop* — they never propagate outward through multiple levels of nesting. To break out of multiple nested loops at once, common approaches are: extract the nested loops into a function and return from the desired point, use a flag variable checked by the outer loop, or (for very specific cases) raise and catch an exception.

for i in range(3):
    for j in range(3):
        if j == 1:
            break        # exits only the inner loop
        print(i, j)
# (0,0) (1,0) (2,0) -- outer loop keeps running normally
# Breaking out of BOTH loops -- extract into a function and return
def find_pair(matrix, target):
    for i, row in enumerate(matrix):
        for j, value in enumerate(row):
            if value == target:
                return (i, j)     # exits both loops immediately
    return None

print(find_pair([[1, 2], [3, 4]], 3))   # (1, 0)

Combining break and continue with else on Loops

A for/while loop's else clause (covered in full in 'else on Loops') runs only when the loop completes *without* hitting a break. This makes break the signal that decides whether else runs — continue, by contrast, has no effect on whether else eventually executes, since it only affects the current iteration, not the loop's overall completion.

def has_duplicate(items):
    seen = set()
    for item in items:
        if item in seen:
            print(f'Duplicate found: {item}')
            break
        seen.add(item)
    else:
        print('No duplicates')

has_duplicate([1, 2, 3, 2])   # Duplicate found: 2
has_duplicate([1, 2, 3])      # No duplicates

Appropriate Use: When Each Belongs

Use break for early-exit search patterns ('stop as soon as we find what we need') — it avoids unnecessary work once the answer is known. Use continue to skip clearly invalid or irrelevant items without deeply nesting the rest of the loop body in an if. Use pass as an intentional placeholder — stub functions, empty exception handlers you plan to fill in, or a branch that is genuinely meant to do nothing.

# continue used to avoid deep nesting (a 'guard clause' inside a loop)
for record in records:
    if not record.is_valid():
        continue                # skip invalid records early
    process(record)             # main logic stays unindented

# versus the more deeply nested equivalent:
for record in records:
    if record.is_valid():
        process(record)         # main logic buried one level deeper

Common Overuse and Misuse

Overusing break/continue to encode complex branching logic can make a loop hard to follow — if a loop body has several breaks scattered at different points for different reasons, consider whether restructuring (e.g. extracting the search into its own function that simply returns) would communicate intent more clearly. pass used purely to silence a linter or as a substitute for actually implementing a case (rather than as a deliberate stub) is a smell worth revisiting.

try:
    risky_operation()
except ValueError:
    pass   # silently swallowing an error -- usually a mistake unless deliberate and documented

# Better: at least log or comment on why it's safe to ignore
try:
    risky_operation()
except ValueError:
    pass  # deliberately ignored: malformed rows are expected and already counted elsewhere

Key Takeaways

break exits the nearest enclosing loop immediately and skips that loop's else clause entirely.

continue skips only the rest of the current iteration and moves on to the next one; it does not affect whether the loop's else clause runs.

pass is a genuine no-op statement, required because Python's syntax never allows a truly empty block body.

Both break and continue apply to the nearest enclosing loop only — use a function with return, or a flag, to escape multiple nested loops at once.

In a while loop, make sure state updates needed for loop progress happen before any continue, or you risk an infinite loop.

Reach for continue to flatten guard-clause-style filtering inside a loop, and reserve bare pass for deliberate placeholders, not silent error-swallowing.

i = 0 while i < 10: if i % 2 != 0: continue # 'i' never gets incremented on this path i += 1 print(i) i = 0 while i < 10: i += 1 # increment BEFORE any continue if i % 2 != 0: continue print(i)

continue jumps straight back to the condition check, skipping any code below it in that iteration. If the loop-progress update sits after the continue, that update never runs for skipped iterations, causing an infinite loop.

for i in range(3): for j in range(3): if j == 1: break # WRONG: only breaks the inner loop, outer keeps going def search(): for i in range(3): for j in range(3): if j == 1: return (i, j) # exits both loops via return search()

break only exits the innermost loop it's directly inside. To exit multiple nested loops at once, extract the loops into a function and return, or use a flag checked by the outer loop.

try: parse(data) except ValueError: pass try: parse(data) except ValueError: logger.warning('Skipping malformed record: %r', data)

Using pass to silently swallow an exception hides real failures. If ignoring the error is genuinely correct, at least log it or leave a clear comment explaining why — otherwise handle it properly.

What is the key difference in effect between break and continue inside a loop body?

break terminates the entire loop immediately, skipping all remaining iterations and the loop's else clause. continue ends only the current iteration and proceeds to the next one; the loop as a whole keeps running.

Why does Python need a pass statement at all — what problem does it solve syntactically?

Python requires every colon-introduced block (if, for, while, def, class, try, etc.) to contain at least one statement; a comment alone doesn't count as a statement. pass is a real no-op statement that satisfies this requirement when a block is intentionally empty.

Why can a continue statement accidentally cause an infinite loop in a while loop, and how do you avoid it?

If the code that updates the loop's controlling state sits after the continue in the loop body, that update is skipped whenever continue fires, so the condition may never become false. Place state updates needed for progress before any continue in the body.

How do break and continue behave differently with respect to a loop's else clause?

break prevents the else clause from running at all (since else only runs on normal, non-break completion). continue has no effect on else — it only skips the current iteration, and the loop can still finish normally afterward and trigger else.

If you need to exit two nested for loops as soon as a condition is met, why won't a single break do it, and what's a clean way to achieve it?

break only exits the nearest enclosing loop — the inner one — leaving the outer loop to continue running. A clean approach is to extract both loops into a function and use return to exit both at once, since return immediately exits the whole function regardless of nesting depth.

Is using pass in an except block ever appropriate? When, and what should accompany it?

Yes, when an exception is genuinely expected and safe to ignore — but it should generally be accompanied by a comment (or a log statement) explaining why it's safe, so future readers don't mistake it for an oversight or lost error information.

What does this print?

1 2 4. When n==3, continue skips printing and moves to n=4 (printed). When n==5, break exits before printing, so 5 never appears.

Write a function first_negative(numbers) that returns the first negative number in a list, or None if there isn't one, using break and a loop's else clause.

def first_negative(numbers: list[int]) -> int | None: for n in numbers: if n < 0: result = n break else: return None return result print(first_negative([3, 7, -2, 9])) # -2 print(first_negative([3, 7, 9])) # None

The loop breaks as soon as a negative number is found, leaving result set; if the loop runs to completion without finding one, break never fires so else runs and returns None directly.

What does this print, and why?

None. A function whose body is just pass executes and returns nothing explicitly, so like any function without an explicit return, it implicitly returns None.

Write nested loops over a 2D grid (list of lists of integers) that find and print the (row, col) position of the first negative number, breaking out of both loops as soon as it's found, using a flag variable (not a function/return).

grid = [[1, 2, 3], [4, -5, 6], [7, 8, 9]] found = False for i, row in enumerate(grid): for j, value in enumerate(row): if value < 0: print(f'Found at ({i}, {j})') found = True break # exits only the inner loop if found: break # outer loop checks the flag and exits too

Since break only exits its immediately enclosing loop, a boolean flag set inside the inner loop is checked right after the inner loop ends, letting the outer loop decide to break as well — a common pattern when extracting to a function isn't convenient.

for n in range(1, 6):
    if n == 3:
        continue
    if n == 5:
        break
    print(n)
def stub_handler():
    pass

print(stub_handler())