break, continue and pass
Loop control, and the mistakes each one invites.
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.
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.
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
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 allusers = [
{"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)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# 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: ...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)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# 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)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, stoppingTip: 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
breakLeaves the nearest enclosing loop immediately. Valid only inside a loop.
if found:\n break
continueSkips the rest of this iteration and goes to the next. Valid only inside a loop.
if invalid:\n continue
passDoes nothing. Valid anywhere a statement is required - if, def, class, except.
class Draft:\n pass
returnLeaves the function, and therefore every loop inside it. Only inside a function.
return row, column
for ... elseThe 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 loopbreak yes · continue no · pass no
Skips this iterationbreak n/a (the loop is over) · continue yes · pass no
Runs the rest of the bodybreak no · continue no · pass yes
Usable outside a loopbreak no · continue no · pass yes
Triggers the loop elsebreak skips it · continue no effect · pass no effect
Typical usebreak: 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.
“for n in range(1,6): if n==2: continue if n==5: break print(n)”
“for n in range(4): if n==2: pass print(n)”
“for n in [1,2]: print(n) else: print("finished")”
“for a in range(2): for b in range(3): if b==1: break print(a,b)”
What usually goes wrong
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 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:
continueIt 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 lessonIt 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 breakexcept 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)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")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
breakBest 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.
From numbers = [10, -5, 20, -3, 30], print only the positive values using continue.
Show solutionHide solution
numbers = [10, -5, 20, -3, 30]
for number in numbers:
if number < 0:
continue
print(number)
# 10 20 30From numbers = [10, 20, 30, 0, 40, 50], print values until a 0 appears, then stop.
Show solutionHide solution
numbers = [10, 20, 30, 0, 40, 50]
for number in numbers:
if number == 0:
break
print(number)
# 10 20 30Search courses = ["Python", "React", "Node.js", "AWS"] for "Node.js". Print "Course found" and stop; print "Not found" if it is absent.
Show hintHide hint
A loop else handles the "never found it" case with no flag.
Show solutionHide solution
courses = ["Python", "React", "Node.js", "AWS"]
for course in courses:
if course == "Node.js":
print("Course found")
break
else:
print("Not found")From scores = [80, None, 90, None, 75], print only the real scores.
Show solutionHide solution
scores = [80, None, 90, None, 75]
for score in scores:
if score is None:
continue
print(score)
# 80 90 75Allow three login attempts. Stop immediately on success. Simulate the second attempt succeeding.
Show solutionHide 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 successfulWrite a function that finds the position of a target in a 5x5 grid and returns it, leaving both loops at once.
Show hintHide hint
return, not break.
Show solutionHide 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)) # NonePredict, then run: for n in range(1, 6) with `continue` at 2 and `break` at 5, printing n.
Show solutionHide solution
for number in range(1, 6):
if number == 2:
continue
if number == 5:
break
print(number)
# 1
# 3
# 4Student 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.
- 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
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 solutionHide 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 groupKey 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
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.
What is the difference between break and continue?
- 2.
Does pass skip an iteration?
- 3.
Does break exit every nested loop?
- 4.
When does the else clause of a for loop run?
- 5.
Why can continue cause an infinite loop in while but not in for?
- 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...
AI
System Design
Backend
- GraphQL8 modules · 69 lessons planned
- Core Python13 modules · 75 lessons planned
- FastAPI5 sections · 20 lessons
- Node.js14 modules · 206 lessons planned
- Node.js Performance7 chapters · 36 topics
- Event Loop Lifecycle6 phases · 3 scenarios
- Docker & Containerization11 modules · 144 lessons planned
- AWS for Developers14 modules · 219 lessons planned
- CI/CD & DevOps Automation10 modules · 134 lessons planned