Skip to main content

Controlling Loops

Sometimes you need more control over how loops execute. You might want to exit a loop early when you find what you're searching for, skip certain iterations that don't meet your criteria, or detect whether a loop completed naturally or was interrupted. Python provides three powerful tools for this: break, continue, and the loop-else clause.

In this lesson, you'll learn how to control loop execution with precision, making your code more efficient and expressive.

Why Loop Control Matters

Consider these real-world scenarios where basic loops aren't enough:

Search and Stop: You're searching a list of 10,000 items for a specific value. Once you find it, there's no point continuing through the remaining items. You need to exit early.

Skip Invalid Data: You're processing user records, but some have missing or invalid fields. Instead of crashing or processing bad data, you want to skip those records and continue with the rest.

Retry with Limit: You're asking a user for input and they keep entering invalid data. You want to give them 3 chances, then give up. You need to know whether they succeeded within the limit or ran out of attempts.

These scenarios require more than simple for and while loops—they need loop control statements.

Breaking Out Early: The break Statement

The break statement immediately exits the loop, skipping any remaining iterations. It's like saying "I found what I was looking for—stop searching."

Basic Break: Search for a Number

# Search for a target number in a list
numbers: list[int] = [3, 7, 12, 5, 19, 8]
target: int = 12

for num in numbers:
if num == target:
print(f"Found {target}!")
break # Exit the loop immediately
print(f"Checking {num}...")

# Output:
# Checking 3...
# Checking 7...
# Found 12!

What happens:

  1. Loop starts, checks 3 (not the target)
  2. Checks 7 (not the target)
  3. Checks 12 (matches!) → prints "Found 12!" → break exits the loop
  4. Numbers 5, 19, 8 are never checked because the loop exited early

💬 AI Colearning Prompt

"Why does break immediately exit the loop instead of just ending the current iteration? What's happening in memory?"

When to Use Break

Use break when:

  • ✅ Searching for a specific item (stop when found)
  • ✅ Validating input with a retry limit (stop when valid)
  • ✅ Processing until a termination condition is met
  • ❌ You need to skip ONE iteration but continue the loop (use continue instead)

🎓 Instructor Commentary

In AI-native development, you don't memorize every use case for break—you understand the early exit pattern: "I got what I needed; no point continuing." When you recognize that pattern in your logic, break is your tool.

Skipping Iterations: The continue Statement

The continue statement skips the rest of the current iteration and moves to the next one. It's like saying "This one doesn't count—move to the next."

Basic Continue: Skip Even Numbers

# Print only odd numbers from 1 to 10
for num in range(1, 11):
if num % 2 == 0: # If the number is even
continue # Skip to next iteration
print(f"{num} is odd")

# Output:
# 1 is odd
# 3 is odd
# 5 is odd
# 7 is odd
# 9 is odd

What happens:

  1. When num = 2 (even), continue skips the print and jumps to num = 3
  2. When num = 3 (odd), condition is False → print executes
  3. Pattern repeats for all numbers

Difference from Break:

  • break → Exit the entire loop
  • continue → Skip the current iteration only

🚀 CoLearning Challenge

Ask your AI Co-Teacher:

"Generate a for loop that prints numbers 1-20, but skips multiples of 3. Then explain how continue works differently from break with a diagram."

Expected Outcome: You'll understand when to skip iterations vs when to exit entirely.

Real-World Continue: Filtering Invalid Data

# Process user records, skip invalid ones
user_records: list[dict[str, str]] = [
{"name": "Alice", "email": "[email protected]"},
{"name": "Bob", "email": ""}, # Invalid: missing email
{"name": "Charlie", "email": "[email protected]"},
{"name": "", "email": "[email protected]"}, # Invalid: missing name
]

for record in user_records:
# Skip records with missing data
if not record["name"] or not record["email"]:
print(f"Skipping invalid record: {record}")
continue

# Process valid records
print(f"Processing {record['name']} ({record['email']})")

# Output:
# Processing Alice ([email protected])
# Skipping invalid record: {'name': 'Bob', 'email': ''}
# Processing Charlie ([email protected])
# Skipping invalid record: {'name': '', 'email': '[email protected]'}

This pattern is common in data validation: check for problems, skip bad data, process good data.

✨ Teaching Tip

Use your AI as code reviewer: Paste a loop with complex if-else logic and ask "Could I simplify this with continue instead of nesting?" You'll learn when early returns make code clearer.

Detecting Completion: The for...else Pattern

Python's loops have an else clause that executes only if the loop completes normally (without hitting break). This is one of Python's most misunderstood but powerful features.

For...Else: Search with "Not Found" Detection

# Search for a name in a list
names: list[str] = ["Alice", "Bob", "Charlie", "Diana"]
search_name: str = "Eve"

for name in names:
if name == search_name:
print(f"Found {search_name}!")
break
else:
# This runs ONLY if break was never called
print(f"{search_name} not found in the list")

# Output:
# Eve not found in the list

How it works:

  • If break is called → else is skipped
  • If loop completes naturally → else executes

Try it with a name that exists:

search_name: str = "Charlie"

for name in names:
if name == search_name:
print(f"Found {search_name}!")
break
else:
print(f"{search_name} not found in the list")

# Output:
# Found Charlie!
# (else clause skipped because break was called)

💬 AI Colearning Prompt

"Why is the loop-else pattern called 'else' if it's really 'if loop completed without break'? Explain the naming reasoning and show alternatives without using else."

Common Mistake: Misunderstanding When Else Runs

❌ WRONG ASSUMPTION: "Else runs if the loop has no items"

# Empty list
empty_list: list[int] = []

for item in empty_list:
print(item)
else:
print("This runs because the loop completed (even though it had 0 iterations)")

# Output:
# This runs because the loop completed (even though it had 0 iterations)

✅ CORRECT UNDERSTANDING: Else runs when the loop completes naturally, whether it had 0 iterations or 1,000.

The only way to prevent the else clause from running is with break.

Why Use For...Else?

Without for...else (using a flag variable):

found: bool = False

for name in names:
if name == search_name:
print(f"Found {search_name}!")
found = True
break

if not found:
print(f"{search_name} not found")

With for...else (cleaner):

for name in names:
if name == search_name:
print(f"Found {search_name}!")
break
else:
print(f"{search_name} not found")

The for...else version is shorter and more readable—no need for a flag variable.

🎓 Instructor Commentary

In AI-native development, the loop-else pattern is rare but powerful. You don't memorize when to use it—you recognize the "search with failure case" scenario. When you need to know "Did I find it or exhaust all options?", loop-else is your answer.

While...Else: Retry with Limit

The while...else pattern works the same way: else runs if the loop exits normally (not via break).

This is perfect for retry scenarios where you want to know if the user succeeded or ran out of attempts.

Retry Input with Limit

# Give user 3 chances to enter a positive number
max_attempts: int = 3
attempt: int = 0

while attempt < max_attempts:
user_input: str = input("Enter a positive number: ")

# Try to convert to int
try:
number: int = int(user_input)
if number > 0:
print(f"Success! You entered {number}")
break # Exit loop on success
else:
print("That's not positive. Try again.")
except ValueError:
print("That's not a valid number. Try again.")

attempt += 1
else:
# This runs ONLY if we exhausted all attempts without break
print("Sorry, you've run out of attempts.")

Execution scenarios:

Scenario 1: User succeeds on attempt 2

Enter a positive number: abc
That's not a valid number. Try again.
Enter a positive number: -5
That's not positive. Try again.
Enter a positive number: 10
Success! You entered 10
(else clause skipped because break was called)

Scenario 2: User fails all 3 attempts

Enter a positive number: abc
That's not a valid number. Try again.
Enter a positive number: -5
That's not positive. Try again.
Enter a positive number: 0
That's not positive. Try again.
Sorry, you've run out of attempts.
(else clause runs because loop completed without break)

Why While...Else?

Without while...else:

success: bool = False

while attempt < max_attempts:
# ... validation logic ...
if valid:
success = True
break
attempt += 1

if not success:
print("Sorry, you've run out of attempts.")

With while...else:

while attempt < max_attempts:
# ... validation logic ...
if valid:
break
attempt += 1
else:
print("Sorry, you've run out of attempts.")

Again, the while...else version eliminates the need for a flag variable.

AI Companion: Complex Loop Control Scenarios

Now that you understand the basics, let's explore more complex patterns with your AI companion.

Combined Break and Continue: Input Validation Loop

Ask your AI to generate this scenario:

🚀 CoLearning Challenge

Ask your AI Co-Teacher:

"Generate a while loop that asks users to enter numbers. Use continue to skip negative numbers (with a warning). Use break to exit when they enter 0. Calculate the sum of positive numbers only. Then explain the flow with a step-by-step trace."

Expected Outcome: You'll understand how break and continue work together in the same loop.

Sample AI-generated solution:

# Sum positive numbers until user enters 0
total: int = 0

while True: # Infinite loop—relies on break to exit
user_input: str = input("Enter a number (0 to quit): ")

try:
number: int = int(user_input)
except ValueError:
print("Invalid input. Try again.")
continue # Skip this iteration

if number == 0:
print(f"Total sum: {total}")
break # Exit the loop

if number < 0:
print("Negative numbers are ignored.")
continue # Skip adding to total

# Only positive numbers reach here
total += number
print(f"Added {number}. Running total: {total}")

Trace execution:

Input: "5" → Valid, positive → total = 5
Input: "-3" → Valid, negative → skip (continue), total = 5
Input: "abc" → Invalid → skip (continue), total = 5
Input: "10" → Valid, positive → total = 15
Input: "0" → Valid, zero → print total (15), break

✨ Teaching Tip

When combining break and continue, ask your AI: "Trace this loop with inputs [5, -3, 'abc', 10, 0]. Show me which line executes for each input." Seeing the step-by-step flow helps you internalize control flow logic.

Red Flags: Common Loop Control Mistakes

🚩 Red Flag 1: Misunderstanding When Else Executes

❌ WRONG:

for i in range(3):
print(i)
else:
print("This runs because loop finished") # Correct behavior

# Student thinks: "Else runs if condition is False"
# Reality: "Else runs if loop completes without break"

✅ AI Troubleshooting Prompt:

"I thought loop-else was like if-else, but it's not. Explain the difference and show when else does NOT run in a for loop."


🚩 Red Flag 2: Using Continue Outside a Loop

❌ ERROR:

numbers: list[int] = [1, 2, 3]

if 2 in numbers:
continue # SyntaxError: 'continue' not properly in loop

Error Message:

SyntaxError: 'continue' not properly in loop

✅ Fix: continue only works inside for or while loops.

✅ AI Troubleshooting Prompt:

"I'm getting 'SyntaxError: continue not properly in loop'. Why can't I use continue in an if statement? Show me the correct pattern."


🚩 Red Flag 3: Break After Print (Unreachable Code)

❌ LOGICAL ERROR:

for i in range(10):
break # Exits immediately on first iteration
print(i) # This NEVER runs

✅ Fix: Put break after the code you want to execute on the final iteration.

✅ Correct Pattern:

for i in range(10):
print(i)
if i == 5:
break # Exit after printing 5

✅ AI Troubleshooting Prompt:

"My loop with break prints nothing. Help me trace the execution and find where break should be placed."


🚩 Red Flag 4: Overusing Break (When Functions Are Better)

❌ UNCLEAR CODE:

for i in range(100):
for j in range(100):
if some_condition:
break # Which loop does this exit?

✅ CLEARER with Functions (coming in Chapter 18):

def find_pair(limit: int) -> tuple[int, int] | None:
for i in range(limit):
for j in range(limit):
if some_condition:
return (i, j) # Exit entire function
return None

Note: You'll learn about functions in the next chapter. For now, know that deeply nested loops with multiple break statements often signal that code should be refactored into separate functions.


🚩 Red Flag 5: Break in Nested Loops Only Exits Inner Loop

❌ COMMON MISTAKE:

# Trying to exit both loops at once
for i in range(5):
for j in range(5):
if i == 2 and j == 3:
break # Only exits the INNER loop (j loop)
# Outer loop (i loop) continues

Output:

# Processes i=0 with all j values
# Processes i=1 with all j values
# Processes i=2, exits inner loop at j=3, but continues i=3, i=4

✅ AI Troubleshooting Prompt:

"I want to exit both nested loops at once, but break only exits the inner loop. What are my options? Explain flag variables vs functions vs other patterns."

✅ Temporary Fix (Using a Flag):

found: bool = False

for i in range(5):
for j in range(5):
if i == 2 and j == 3:
found = True
break # Exit inner loop
if found:
break # Exit outer loop

(Better solutions involve functions with return, which you'll learn in Chapter 18.)

Try With AI

Now that you understand loop control statements, reinforce your learning by working with your AI companion. Use ChatGPT web (if you haven't set up an AI tool yet) or your preferred AI companion (Gemini CLI, Claude CLI, etc.).

1. Recall: Break vs Continue

Prompt:

"What's the difference between break and continue in Python loops? Give me a simple example showing both in the same loop."

Expected Outcome: Your AI will explain that break exits the entire loop while continue skips the current iteration and moves to the next. You should see a code example demonstrating both.


2. Understand: Trace For...Else Execution

Prompt:

"Trace this code step-by-step and tell me whether the else clause runs:

numbers = [2, 4, 6, 8]
for num in numbers:
if num == 5:
print('Found 5!')
break
else:
print('5 not found')

Explain WHY the else clause does or doesn't run."

Expected Outcome: Your AI will trace each iteration, show that 5 is never found, break is never called, and therefore the else clause executes, printing "5 not found".


3. Apply: Retry Logic with While...Else

Prompt:

"Generate a while loop that asks a user to guess a secret number (7). Give them 3 attempts. If they guess correctly, use break and print 'Correct!'. If they run out of attempts, use the else clause to print 'Out of attempts, the answer was 7'. Include type hints and input validation."

Expected Outcome: Your AI will generate code similar to the retry pattern you learned earlier. Test it by entering wrong guesses (e.g., 5, 3, 9) to trigger the else clause, then try again with a correct guess to trigger the break.


4. Evaluate: For...Else vs Flag Variable

Prompt:

"Compare these two approaches for searching a list:

Approach 1 (flag variable):

found = False
for item in items:
if item == target:
found = True
break
if not found:
print('Not found')

Approach 2 (for...else):

for item in items:
if item == target:
break
else:
print('Not found')

When is for...else more elegant? When might a flag variable be clearer? Give me an example where each is better."

Expected Outcome: Your AI will explain that for...else is more concise for simple search patterns, but flag variables can be clearer when you need to check the result in multiple places or when the logic is complex. You'll gain intuition for when to use each pattern.


Safety & Ethics Note: Loop control statements can create infinite loops or skip critical validation logic if used incorrectly. Always trace your loop logic manually or with your AI before deploying code that controls retries, validation, or search operations. Ask your AI to review your loop control flow for potential bugs.