← Back to Core Python
Lesson 14 · Operators and Control Flow

break, continue and pass

Loop control, and the mistakes each one invites.

Beginner30 min

What you will be able to do

  • Stop a loop early with break, and skip one iteration with continue
  • Use pass as a deliberate placeholder, and know it is not continue
  • Escape nested loops, where break alone will not do it
  • Explain when a loop else clause runs, and why it is useful for searches
  • Avoid the continue-before-increment infinite loop in a while
  • Use continue as a guard clause to keep the main path unindented

The idea, in plain English

A loop normally runs to the end of its sequence. These three statements interrupt that, and the whole lesson is about knowing exactly where control goes next.

break leaves the loop immediately - nothing else in the body runs, and the loop does not resume. continue abandons the current iteration only; the loop carries on with the next item. pass does nothing at all, and exists purely because Python needs a statement where a block is required.

The confusion is between pass and continue. They look interchangeable because both appear to mean "skip this", and they are not: after pass, execution carries straight on to the next line in the same iteration. It is the difference between saying nothing and walking out of the room.

The second thing to internalise is that break is single-storey. In nested loops it exits the innermost one only, and the outer loop keeps going as though nothing happened.

Worked example: Finding a user and stopping the search the moment you do.

Three statements, three destinations

The only question that matters is where the next line of code executes. Answer that for each statement and you will never confuse them.

break jumps past the end of the loop. continue jumps back to the top of the loop for the next item. pass jumps nowhere - the next statement in the body runs, which is usually not what a beginner writing pass intends.

Where control goes next
breakOut of the loop entirely. The rest of the body is skipped and the loop does not resume.
continueBack to the top for the next item. The rest of this iteration is skipped, the loop keeps going.
passNowhere. The next line in the same iteration runs, exactly as if pass were not there.
returnOut of the whole function, loop included. Often the cleanest way to leave nested loops.
Nested loopsbreak and continue apply to the nearest enclosing loop only.

Watch out: pass is not a weaker continue. `if not user["active"]: pass` does nothing, so the inactive user falls straight through to the code below it.

break only leaves one loop

In nested loops, break exits the loop it is written in. The outer loop continues with its next item, which surprises people the first time they use it for a two-dimensional search.

Two ways out. A flag - set it in the inner loop, break, then check it immediately after and break again - works but adds a variable and a second check for every level of nesting.

Moving the search into a function and using return is usually better: return leaves everything at once regardless of depth, the function has an obvious name, and the result comes back as a value instead of a leftover variable.

Getting out of two loops
break aloneLeaves the inner loop. The outer loop continues - almost never what a search wants.
A flagfound = True; break, then `if found: break` outside. Works, and gets noisy past two levels.
return from a functionLeaves every loop at once and hands back the result. Usually the clearest option.
itertools.productFlattens nested loops into one, so a single break ends it. Good when the loops are independent.
An exceptionWorks, and is reserved for genuinely exceptional exits - not ordinary control flow.

The loop else clause

A for or while loop can have an else, and it does not mean what the word suggests. It runs when the loop finishes normally - that is, when no break occurred. If the loop breaks, the else is skipped.

Read it as "no break" rather than "else", and its one good use becomes obvious: searching. The loop breaks when it finds the thing; the else handles "went through everything and never found it", with no found flag to declare, set and re-check.

continue does not affect it. Skipping iterations still counts as finishing normally, so the else runs.

Tip: Not every team uses loop else - it is genuinely easy to misread. If your reviewers flinch at it, a function with return is just as concise and needs no explanation.

continue before the increment is an infinite loop

In a for loop, continue is safe - the iterator advances on its own. In a while loop, nothing advances unless you advance it, and continue jumps straight back to the condition, skipping whatever update sat below it.

So `while n < 5: if n == 3: continue; n += 1` hangs forever: at 3 it skips the increment, the condition is re-tested with 3 unchanged, and round it goes.

The fix is to update the state before any continue in the body. The safer habit is to prefer a for loop whenever the number of iterations is known, and keep while for the genuinely open-ended cases.

Syntax and examples

The three, side by side
for number in range(1, 6): if number == 3: break print(number) # 1 2 - the loop ended at 3 for number in range(1, 6): if number == 3: continue print(number) # 1 2 4 5 - only 3 was skipped for number in range(1, 6): if number == 3: pass print(number) # 1 2 3 4 5 - pass did nothing at all
break: stop as soon as you have the answer
users = [ {"id": 101, "name": "Ravi"}, {"id": 102, "name": "Anita"}, {"id": 103, "name": "Chandu"}, {"id": 104, "name": "Priya"}, ] found = None for user in users: if user["id"] == 103: found = user break # Priya is never examined print(found) # {'id': 103, 'name': 'Chandu'} # while True plus break is the standard interactive loop while True: command = input("Enter command: ") if command == "exit": break print("You entered:", command)
continue: skip what you cannot process
scores = [80, 92, None, 75, None, 88] for score in scores: if score is None: continue print("Score:", score) # 80 92 75 88 # As a guard clause, continue flattens what would be nested ifs users = [ {"name": "Ravi", "active": True, "email": "ravi@example.com"}, {"name": "Anita", "active": False, "email": "a@example.com"}, {"name": "Chandu", "active": True, "email": ""}, ] for user in users: if not user["active"]: continue if not user["email"]: continue print("Emailing", user["name"]) # Emailing Ravi
pass: a block that has to exist
# Python needs a statement after a colon, so these would be SyntaxErrors def authenticate_user(): pass class Student: pass # Deliberately ignoring an error - and this one deserves a comment saying why try: value = int("abc") except ValueError: pass # a bad value is expected here; the default below covers it # ... is also valid and common in type stubs def create_course() -> None: ...
break in nested loops leaves only the inner one
for row in range(3): for column in range(5): if column == 2: break # ends this inner loop only print(row, column) # 0 0 / 0 1 / 1 0 / 1 1 / 2 0 / 2 1 - the outer loop ran all three times # A flag gets you out of both found = None for row in range(5): for column in range(5): if row == 2 and column == 3: found = (row, column) break if found: break print(found) # (2, 3) # return is cleaner, and scales past two levels def find_position(target_row, target_column): for row in range(5): for column in range(5): if row == target_row and column == target_column: return row, column return None print(find_position(2, 3)) # (2, 3)
Loop else means "no break happened"
numbers = [10, 20, 30, 40] for number in numbers: if number == 25: print("Found") break else: print("Not found") # runs - the loop never broke # Which replaces the found-flag dance found = False for number in numbers: if number == 25: found = True break if not found: print("Not found") # continue does not stop the else from running for number in range(5): if number == 2: continue print(number) else: print("Loop completed") # 0 1 3 4, then Loop completed
The while + continue infinite loop
# WRONG - hangs at 3 forever, because the increment is skipped # number = 0 # while number < 5: # if number == 3: # continue # number += 1 # RIGHT - advance the state before any continue number = 0 while number < 5: number += 1 if number == 3: continue print(number) # 1 2 4 5 # In a for loop this cannot happen - the iterator advances itself for number in range(5): if number == 3: continue print(number)
Both together: process, skip, and stop
students = [ {"name": "Ravi", "score": 80, "active": True}, {"name": "Anita", "score": None, "active": True}, {"name": "Chandu", "score": 95, "active": False}, {"name": "Priya", "score": 88, "active": True}, {"name": "Meera", "score": 96, "active": True}, ] for student in students: if not student["active"]: continue # skip - not our concern if student["score"] is None: continue # skip - nothing to report print(f"{student['name']}: {student['score']}") if student["score"] >= 95: print("Top score reached, stopping") break # stop - we have what we came for else: print("Processed everyone without a top score") # Ravi: 80 # Priya: 88 # Meera: 96 # Top score reached, stopping

Tip: Several continues in a row at the top of a loop body is a good shape, not a smell. It is the guard-clause pattern from lesson 10, and it keeps the real work unindented at the bottom.

The four control statements

break

Leaves the nearest enclosing loop immediately. Valid only inside a loop.

if found:\n    break
continue

Skips the rest of this iteration and goes to the next. Valid only inside a loop.

if invalid:\n    continue
pass

Does nothing. Valid anywhere a statement is required - if, def, class, except.

class Draft:\n    pass
return

Leaves the function, and therefore every loop inside it. Only inside a function.

return row, column
for ... else

The else runs when the loop finishes without a break. Read it as "no break".

for x in xs:\n    ...\nelse:\n    ...
...

The Ellipsis literal, used like pass in stubs and protocol definitions.

def f() -> None: ...

break, continue, pass compared

Ends the loop

break yes · continue no · pass no

Skips this iteration

break n/a (the loop is over) · continue yes · pass no

Runs the rest of the body

break no · continue no · pass yes

Usable outside a loop

break no · continue no · pass yes

Triggers the loop else

break skips it · continue no effect · pass no effect

Typical use

break: early exit · continue: filtering · pass: placeholder

Try it yourself

The code does not change. Swap the content string and the program does something else entirely.

All three on one loop

“for n in range(1,6): if n==2: continue if n==5: break print(n)”

pass is not continue

“for n in range(4): if n==2: pass print(n)”

else means no break

“for n in [1,2]: print(n) else: print("finished")”

break leaves one loop

“for a in range(2): for b in range(3): if b==1: break print(a,b)”

What usually goes wrong

Using pass where you meant continue

pass does nothing, so execution carries straight on to the next line in the same iteration. The record you meant to skip gets processed anyway, with no error.

✗ for user in users:
    if not user["active"]:
        pass
    send_notification(user)
✓ for user in users:
    if not user["active"]:
        continue
    send_notification(user)
continue before the increment in a while loop

continue jumps back to the condition, skipping the update below it. The variable never changes and the loop runs forever. A for loop cannot do this, because the iterator advances itself.

✗ while n < 10:
    if n == 5:
        continue
    n += 1
✓ while n < 10:
    n += 1
    if n == 5:
        continue
Expecting break to exit every nested loop

It leaves the loop it is written in and no more. The outer loop carries on with its next item as though nothing happened.

✗ for course in courses:
    for lesson in course:
        if done:
            break      # only ends the lesson loop
✓ def find(courses):
    for course in courses:
        for lesson in course:
            if done:
                return lesson
Reading loop else as "otherwise"

It runs when the loop completes without a break - not when the loop body never ran, and not when a condition failed. Say "no break" in your head every time you read one.

✗ # "the else runs if the list was empty"
✓ # the else runs if the loop never hit break
Swallowing an exception with a bare pass

except ValueError: pass hides the failure completely - no message, no log, no clue. Sometimes it is right, and when it is, the reason belongs in a comment.

✗ try:
    parse(row)
except ValueError:
    pass
✓ try:
    parse(row)
except ValueError:
    logger.warning("skipping bad row %s", row)
Leaving pass in finished code

It is a placeholder. Left behind, it silently marks a branch that was never implemented, and reads as though it were deliberate.

✗ if user.is_suspended:
    pass
✓ if user.is_suspended:
    raise PermissionError("account suspended")
Searching without break

Without it the loop keeps going after the match, and if the variable is reassigned each time, the last match wins rather than the first. It also does unnecessary work on a long list.

✗ for u in users:
    if u["id"] == target:
        found = u
✓ for u in users:
    if u["id"] == target:
        found = u
        break

Best practices

  • Use break the moment further work is pointless - a found item, a successful retry, a user asking to quit.
  • Use continue as a guard clause so the main path stays unindented.
  • Never use pass to skip an iteration; that is what continue is for.
  • Prefer a function with return over flags when leaving nested loops.
  • In a while loop, update the state before any continue.
  • Prefer for over while whenever the number of iterations is known.
  • Use for/else for searches, or a function with return if your team finds else confusing.
  • Comment any bare except: pass with the reason the error is safe to ignore.

Practice

Write these yourself before opening anything. Getting them wrong first is most of how this sticks.

1.

From numbers = [10, -5, 20, -3, 30], print only the positive values using continue.

Show solution
numbers = [10, -5, 20, -3, 30] for number in numbers: if number < 0: continue print(number) # 10 20 30
2.

From numbers = [10, 20, 30, 0, 40, 50], print values until a 0 appears, then stop.

Show solution
numbers = [10, 20, 30, 0, 40, 50] for number in numbers: if number == 0: break print(number) # 10 20 30
3.

Search courses = ["Python", "React", "Node.js", "AWS"] for "Node.js". Print "Course found" and stop; print "Not found" if it is absent.

Show hint

A loop else handles the "never found it" case with no flag.

Show solution
courses = ["Python", "React", "Node.js", "AWS"] for course in courses: if course == "Node.js": print("Course found") break else: print("Not found")
4.

From scores = [80, None, 90, None, 75], print only the real scores.

Show solution
scores = [80, None, 90, None, 75] for score in scores: if score is None: continue print(score) # 80 90 75
5.

Allow three login attempts. Stop immediately on success. Simulate the second attempt succeeding.

Show solution
for attempt in range(1, 4): print(f"Attempt {attempt}") success = attempt == 2 # pretend the second one works if success: print("Login successful") break else: print("All attempts failed") # Attempt 1 # Attempt 2 # Login successful
6.

Write a function that finds the position of a target in a 5x5 grid and returns it, leaving both loops at once.

Show hint

return, not break.

Show solution
def find_position(target_row, target_column): for row in range(5): for column in range(5): if (row, column) == (target_row, target_column): return row, column return None print(find_position(2, 3)) # (2, 3) print(find_position(9, 9)) # None
7.

Predict, then run: for n in range(1, 6) with `continue` at 2 and `break` at 5, printing n.

Show solution
for number in range(1, 6): if number == 2: continue if number == 5: break print(number) # 1 # 3 # 4
Coding challenge

Student processing system

Work through a list of students, skipping the ones you cannot process and stopping once you find a top score. The interesting part is what happens when no top score exists - handle that without a flag.

It should
  • Skip inactive students
  • Skip students with no score recorded
  • Print every student you do process, with their score
  • Stop as soon as a score of 95 or above is found, and say so
  • If the list runs out with no top score, print that instead - using a loop else, not a flag
  • Run it against both lists below and check the two outcomes differ
Start here
with_top = [ {"name": "Ravi", "score": 80, "active": True}, {"name": "Anita", "score": None, "active": True}, {"name": "Chandu", "score": 95, "active": False}, {"name": "Priya", "score": 88, "active": True}, {"name": "Meera", "score": 96, "active": True}, ] without_top = with_top[:-1] # same list, minus Meera # process(with_top) and process(without_top)
Show one solution
One solution
def process(students): for student in students: # Guard clauses first - the real work stays unindented below. if not student["active"]: continue if student["score"] is None: continue print(f" {student['name']}: {student['score']}") if student["score"] >= 95: print(" Top score found, stopping") break else: # Reached only when the loop was never broken out of. print(" No top score in this group") with_top = [ {"name": "Ravi", "score": 80, "active": True}, {"name": "Anita", "score": None, "active": True}, {"name": "Chandu", "score": 95, "active": False}, {"name": "Priya", "score": 88, "active": True}, {"name": "Meera", "score": 96, "active": True}, ] without_top = with_top[:-1] print("With a top score:") process(with_top) print("Without one:") process(without_top) # With a top score: # Ravi: 80 # Priya: 88 # Meera: 96 # Top score found, stopping # Without one: # Ravi: 80 # Priya: 88 # No top score in this group

Key points

  • break leaves the loop immediately; the rest of the body and the rest of the sequence are skipped.
  • continue abandons the current iteration only and carries on with the next.
  • pass does nothing - the next line in the same iteration still runs.
  • pass is not a weaker continue; confusing them lets unwanted records through silently.
  • break and continue apply to the nearest enclosing loop only.
  • To leave nested loops, use a flag or - better - a function with return.
  • A loop else runs when the loop finishes without a break. Read it as "no break".
  • continue does not prevent the else from running, because the loop still completes.
  • In a while loop, a continue placed before the update causes an infinite loop.
  • A for loop cannot hit that problem, because the iterator advances itself.
  • Several continues at the top of a body is the guard-clause pattern, and it is good style.
  • pass belongs in stubs and placeholders, never in finished branching code.

Quick check before you move on

What does this print? for n in range(1, 6): if n == 2: continue if n == 5: break print(n)
1, 3, 4. Two is skipped by continue, and the loop ends at five before printing.
What does `for n in range(5): if n == 2: pass` then `print(n)` produce?
0 1 2 3 4 - every value. pass does nothing, so the print on the next line runs for 2 as well.
When does a loop else run?
When the loop finishes without executing a break. A continue does not stop it, and neither does an empty sequence - only break does.
Why does `while n < 10: if n == 5: continue; n += 1` hang?
At 5, continue jumps back to the condition and skips the increment, so n stays 5 forever. Move the increment above the continue.

Interview questions

How do you break out of nested loops in Python?

There is no labelled break. The options are a flag checked after the inner loop, moving the search into a function and using return, flattening the loops with itertools.product so one break ends everything, or raising a custom exception. return is usually best: it scales past two levels, names the operation, and hands the result back as a value instead of leaving a variable behind.

Explain the loop else clause and when you would use it.

It runs when the loop terminates normally rather than via break. The canonical use is search: break when you find the item, and the else handles "not found" with no flag to declare, set and re-check. The name is the problem - it reads as "otherwise" and means "no break" - which is why plenty of style guides avoid it. A function with return is equally concise and needs no explanation.

When is a bare `except: pass` acceptable?

Rarely, and only when the failure is genuinely expected and irrelevant - a best-effort cache write, a cleanup that may already have happened. Even then catch the specific exception, not everything, and leave a comment explaining why it is safe. A bare pass on a broad except swallows KeyboardInterrupt and real bugs alike, and produces no evidence that anything went wrong.

Is using continue as a guard clause good practice?

Yes. Several continues at the top of a loop body do the same job as guard clauses in a function: each disqualifying case is handled where it is detected, and the main work stays at one level of indentation. The alternative - nesting everything inside successive ifs - grows a staircase and puts the real logic furthest to the right.

What is the difference between pass, continue and ... in Python?

pass is a no-op statement that satisfies the requirement for a block. continue is loop control and skips to the next iteration. ... is the Ellipsis object, which as a statement also does nothing - it is conventional in type stubs and Protocol definitions, where it reads as "body deliberately omitted" rather than "not implemented yet".

When would you choose while True with a break over a condition in the while?

When the exit condition can only be evaluated part-way through the body - reading until a sentinel arrives, retrying until something succeeds, an interactive prompt. Writing it as a condition would mean duplicating the read before the loop and again at the bottom. The cost is that the exit is no longer visible in the header, so the break should be near the top of the body and obvious.

Quiz

  1. 1.

    What is the difference between break and continue?

  2. 2.

    Does pass skip an iteration?

  3. 3.

    Does break exit every nested loop?

  4. 4.

    When does the else clause of a for loop run?

  5. 5.

    Why can continue cause an infinite loop in while but not in for?

  6. 6.

    Where can pass be used that break and continue cannot?

Comments

Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.

Loading comments...