Control flow · about 35 min

break, continue and loop else

Why this matters

Loops often need to stop early or skip items. Searching for something should stop as soon as it is found, not keep looking through a million more items. Processing a config file should skip comments and blank lines. Python gives loops three tools for this: break exits a loop immediately, continue jumps to the next iteration, and the loop else clause, a Python specialty, runs code only when a loop finished without breaking, which is exactly the "not found" case of a search.

break: leave the loop now

break ends the innermost loop immediately. Execution continues with the first statement after the loop:

for word in "the quick brown fox".split():
    if word == "brown":
        print("found brown")
        break
    print("checked", word)
print("after the loop")
checked the
checked quick
found brown
after the loop

fox was never examined. In a while True: loop, break is the only way out (other than an exception), which is why that pattern always contains one:

while True:
    command = input()
    if command == "quit":
        break
    print(f"running {command}")
print("bye")

With input lines ls, pwd, quit:

running ls
running pwd
bye

continue: skip to the next iteration

continue abandons the rest of the current iteration and goes straight to the next one. In a for loop that means taking the next item; in a while loop it means testing the condition again.

for n in range(1, 11):
    if n % 2 == 0:
        continue
    print(n, end=" ")
print()
1 3 5 7 9

continue is most useful at the top of a loop body as a guard: filter out the items you do not care about, then handle the interesting ones without an extra level of indentation:

k = int(input())
for _ in range(k):
    line = input().strip()
    if not line or line.startswith("#"):
        continue           # skip blank lines and comments
    print("processing:", line)

Without continue, the processing would have to be nested inside an if, and with several filters the nesting gets deep.

Warning: In a while loop, continue skips everything below it, including the update of the loop variable if that comes later in the body. i = 0; while i < 5: if i == 2: continue; i += 1 loops forever once i is 2. Put the update before the continue, or use a for loop.

The loop else clause

Both for and while may have an else block. It runs when the loop ends normally, that is, when a for runs out of items or a while condition becomes false, and it is skipped if the loop ended with break.

target = 7
for n in [3, 9, 4]:
    if n == target:
        print("found it")
        break
else:
    print("not found")
not found

That is a list [3, 9, 4], a collection written in square brackets that you will study in the collections module; looping over it gives each element in turn.

The else is aligned with the for, not with the if. The flow is:

 for ... :                  loop ran out of items  -->  else block runs
     ...
     break   ------------>  skip else, continue after the whole statement
 else:
     ...

The name else is admittedly confusing. A good way to remember it: think "no break". The loop else is designed for searches: the loop looks for something and breaks when it finds it; the else handles "searched everything, found nothing".

Without for-else, you need a flag variable:

found = False
for n in [3, 9, 4]:
    if n == target:
        found = True
        break
if not found:
    print("not found")

Both are correct. The flag version is more familiar to programmers from other languages; the else version is shorter and keeps the logic in one statement. Use whichever your readers will understand; in this track you should at least be able to read both.

A classic: primality testing

n = int(input())
for d in range(2, n):
    if n % d == 0:
        print(f"{n} is divisible by {d}")
        break
else:
    print(f"{n} is prime")

With 21: 21 is divisible by 3. With 23: 23 is prime. For 2 the range is empty, the loop body never runs, no break happens, and the else correctly reports that 2 is prime.

This loop tries every number below n, which is slow for big n. If n = a * b with a <= b, then a * a <= n, so a divisor, if there is one, will be found at or below the square root. Stopping at int(n ** 0.5) + 1 turns a trillion iterations into a million, the kind of improvement that matters far more than any micro-optimization.

break in nested loops

break and continue affect only the innermost loop that contains them:

for row in range(3):
    for col in range(3):
        if col == 1:
            break
        print(row, col)
0 0
1 0
2 0

Each break ended only the inner loop; the outer loop carried on with the next row. To leave both loops at once, the cleanest tool is a function with return (functions come next module). Until then, use a flag that the outer loop checks, or the for-else trick:

for a in range(1, 20):
    for b in range(a, 20):
        if a * a + b * b == 13 * 13:
            print(f"found {a}, {b}")
            break
    else:
        continue      # inner loop did not break: try the next a
    break             # inner loop broke: break the outer loop too
found 5, 12

This works but is hard to read; prefer a function once you know them.

Common mistakes

Recap

Going further: read "break and continue Statements, and else Clauses on Loops" in the official Python tutorial (section 4.4). Then rewrite the nested-loop example above using a flag variable and decide which version you find clearer.

Exercises

Exercise 1 · quiz

Trace break and continue

What does this print? Type the numbers separated by single spaces.

for n in range(1, 10):
    if n % 3 == 0:
        continue
    if n == 8:
        break
    print(n, end=" ")
Answer

continue skips the rest of the body for 3 and 6, so they are never printed. When n reaches 8, break ends the loop at once, so 8 and 9 are not printed either (9 would have been skipped by continue anyway). The result is 1 2 4 5 7.

Exercise 2 · write code

Find the first negative

Read one line containing whole numbers separated by spaces. Print the position (counting from 1) and value of the first negative number, in this format:

first negative: -4 at position 3

(That is for the input 5 8 -4 2 -9.) If there is no negative number, print no negatives.

Use a for loop with break, and the loop's else clause for the "not found" case. To loop over the numbers, split the line: for word in input().split(): gives each number as a string.

File: main.py

Starting point

position = 0
for word in input().split():
    position += 1
    n = int(word)
    if n < 0:
        print(f"first negative: {n} at position {position}")

Example

input

5 8 -4 2 -9

expected output

first negative: -4 at position 3

Example

input

1 2 3

expected output

no negatives
Hint 1

The starter prints every negative. break after the first one stops the loop.

Hint 2

A loop's else block runs only if the loop finished without break. Put print("no negatives") there.

Hint 3

The else lines up with the for, not with the if.

Reference solution
position = 0
for word in input().split():
    position += 1
    n = int(word)
    if n < 0:
        print(f"first negative: {n} at position {position}")
        break
else:
    print("no negatives")

Exercise 3 · write code

Skip blank lines and comments

The first input line is a whole number k: how many more lines follow. Each of the next k lines is a setting like name = value, a comment starting with # (possibly after leading spaces), or blank. For every setting line, print it as name -> value, with the spaces around both parts removed. Skip comments and blank lines using continue. At the end print settings: N where N is how many settings were printed.

For this input:

5
# config file
host = example.org

  # port comes next
port=8080

the output must be exactly:

host -> example.org
port -> 8080
settings: 2

line.partition("=") splits at the first = and returns three parts (before, the =, after); use [0] and [2] on its result.

File: main.py

Starting point

k = int(input())
count = 0
for _ in range(k):
    line = input()
    name = line.partition("=")[0].strip()
    value = line.partition("=")[2].strip()
    print(f"{name} -> {value}")
    count += 1
print(f"settings: {count}")

Example

input

5
# config file
host = example.org

  # port comes next
port=8080

expected output

host -> example.org
port -> 8080
settings: 2

Example

input

2
# a
#b

expected output

settings: 0
Hint 1

Strip each line first, so that indented comments start with # too.

Hint 2

if line == "" or line.startswith("#"): continue skips to the next line.

Reference solution
k = int(input())
count = 0
for _ in range(k):
    line = input().strip()
    if line == "" or line.startswith("#"):
        continue
    name = line.partition("=")[0].strip()
    value = line.partition("=")[2].strip()
    print(f"{name} -> {value}")
    count += 1
print(f"settings: {count}")

Exercise 4 · write code

Prime test with for-else

Read a whole number n (2 or more). If it is prime, print N is prime. Otherwise print N = A x B, where A is its smallest divisor greater than 1 and B is N // A. Replace N, A and B with the numbers. For 91:

91 = 7 x 13

For 13:

13 is prime

Use a for loop over candidate divisors with break, and put the "is prime" message in the loop's else. The checks include a large prime, so stop testing once d * d > n (loop over range(2, ...) with a suitable stop; int(n ** 0.5) + 1 is fine for these sizes).

File: main.py

Starting point

n = int(input())
for d in range(2, n):
    if n % d == 0:
        print(f"{n} = {d} x {n // d}")

Example

input

91

expected output

91 = 7 x 13

Example

input

13

expected output

13 is prime
Hint 1

If n has a divisor at all, it has one no larger than its square root, so the loop can stop there.

Hint 2

break as soon as you find a divisor; the first one found is the smallest.

Hint 3

The else: of the for runs only when no break happened, which means no divisor was found.

Reference solution
n = int(input())
for d in range(2, int(n ** 0.5) + 1):
    if n % d == 0:
        print(f"{n} = {d} x {n // d}")
        break
else:
    print(f"{n} is prime")