Loop control and loop else

Beginner25 min

Steer loops with break and continue, understand how nested loops behave, and use the else clause on a loop to detect a search that found nothing.

Builds on
What you will learn
  • Use break and continue in for and while loops
  • Predict what break does in a nested loop
  • Explain when a loop's else clause runs
  • Choose between a flag variable and for ... else
On this page

You have already met break and continue. This lesson pins down exactly what they do, especially with nested loops, and introduces a feature that surprises newcomers and is rare in other languages: else on a loop.

break, continue and nesting

  • break ends the innermost loop that contains it, and execution carries on after that loop.
  • continue abandons the rest of the current pass of the innermost loop and moves on to the next one.

Neither affects an outer loop. Python has no labelled break, so to leave several loops at once you usually move the loops into a function and use return, which the functions module covers.

nested_control.py
for i in range(1, 4):
for j in range(1, 4):
if j == 2:
continue
if i == 3:
break
print(i, j)
Output
1 1
1 3
2 1
2 3

continue skips j == 2 in every row. When i is 3, break stops the inner loop at its very first pass, so nothing prints for row 3, but the outer loop still finishes normally.

Searching with a flag

A search loop has two outcomes: you found what you wanted, or you ran out of places to look. The classic way to tell them apart is a flag: a variable set to False before the loop, flipped to True before break, and tested afterwards.

search_flag.py
found = False
for letter in "river":
if letter == "v":
found = True
break
print("flag version:", found)
Output
flag version: True

The else clause on a loop

A for or while loop can have an else block. It runs when the loop finishes without hitting break. Read it as “no break”: if the loop was cut short, else is skipped; if it ran out of items or its condition went false, else runs.

That makes it a natural fit for searches, where the interesting question is “did I find it?”.

search_else.py
for letter in "river":
if letter == "z":
print("found z")
break
else:
print("else version: no z in the word")
Output
else version: no z in the word

If the word had a z, the loop would print and break, skipping the else. Since none exists, the loop ends normally and the else reports the failure.

A flag is more familiar and often easier to read. for ... else saves a variable but is unfamiliar to many readers, so give it a comment if your team may not know it.

Searching in practice

Nested loops and else together test whether a number is prime. The inner loop looks for a divisor, and break leaves it as soon as one is found. The else runs only when no divisor was found, which means the number is prime.

primes.py
for n in range(2, 20):
for divisor in range(2, n):
if n % divisor == 0:
break
else:
print(n, end=" ")
print()
Output
2 3 5 7 11 13 17 19

Look at the indentation: the else lines up with the inner for, not with the if, so it belongs to the loop. For n = 2 the inner range is empty, so the loop ends without a break and 2 counts as prime.

The else can belong to the outer loop too. Here it reports that a search over several numbers found nothing:

outer_else.py
for n in range(24, 29):
for divisor in range(2, n):
if n % divisor == 0:
break
else:
print("first prime:", n)
break
else:
print("no primes from 24 to 28")
Output
no primes from 24 to 28

None of 24 to 28 is prime, so the inner else never runs and the outer loop finishes without a break. Change the range to range(24, 30) and it prints first prime: 29 instead.

Common pitfalls

  • Expecting break to exit every loop. It only leaves the innermost one.
  • Misreading the else indentation. An else lined up with for belongs to the loop. One lined up with if belongs to the if. Mixing them up flips the logic.
  • Thinking loop else means “if the loop was empty”. It runs whenever there was no break, including after a normal, non-empty run.
  • Putting the update after continue in a while. The skipped update can cause an infinite loop.
  • Using for ... else with no break in the loop. Then the else always runs, so it is pointless. Without a break, it is just ordinary code after the loop.
ExercisePredict the continue and break

Predict what this program prints, then run it. Input: none. Expected output: four numbers on one line.

predict_control.py
for n in range(1, 10):
if n % 2 == 0:
continue
if n > 7:
break
print(n, end=" ")
print()
Show solution
1 3 5 7

Even numbers are skipped by continue. The odd numbers 1, 3, 5 and 7 are printed. When n reaches 9, it is odd, so continue does not apply, but 9 > 7 triggers break before the print.

ExerciseDoes the word contain a digit?

Given word = "route66", use a for loop with else to print has a digit if any character is a digit, and no digits otherwise. Use the string method isdigit().

Input: route66, then try route. Expected output: has a digit for the first and no digits for the second. The file shows the run for route66.

Show solution
has_digit.py
word = "route66"
for character in word:
if character.isdigit():
print("has a digit")
break
else:
print("no digits")
Output
has a digit

The loop stops at the first 6 and break skips the else. With "route" the loop reaches the end without a break, so the else prints no digits.

ExerciseFirst prime from 90

Find the first prime number from 90 upwards, searching up to 119, and print it as first prime: followed by the number. If none is found, print none found. Use nested loops and else.

Input: none. Expected output: first prime: 97.

Show solution
first_prime.py
for n in range(90, 120):
for divisor in range(2, n):
if n % divisor == 0:
break
else:
print("first prime:", n)
break
else:
print("none found")
Output
first prime: 97

The inner else runs for a number with no divisor, prints it, and the outer break ends the search. The outer else would only run if all 30 numbers had a divisor.

Summary

  • break leaves the innermost loop; continue skips to its next pass.
  • A loop’s else runs only if the loop ended without break, which makes it a “not found” branch for searches.
  • A flag variable achieves the same result and may read more clearly. Functions will give you a third option, an early return.

Next: match statements.