Skip to content
VLSI Mentor

Python Part I · Module 10 · Loops

The else Clause on a Loop

Chapter 10.7 left a loose end. A loop that may stop early has two ways to end — it found what it was looking for, or it ran out of items — and afterwards the code has no way to tell which happened. The fix used there was a flag: declare it before the loop, set it at the break, check it after. Three lines of bookkeeping for something the loop already knows. Python has a construct that removes them, and it is the single strangest piece of syntax in the language: a loop can carry an else. Almost everyone's first reading of it is wrong, because the keyword is a poor name for what it does, so this chapter settles what it pairs with before showing what it is for.

Intermediate14 min readPythonPart IFundamentalsLoopsControl Flow

Part I · Module 10 · Chapter 10.8 · The else Clause on a Loop

1. The Flag This Replaces

From the end of Chapter 10.7 — a search that must report not-found:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lines = ["INFO start", "INFO end"]

found = False

for line in lines:
    if "PASS" in line:
        print("found:", line)
        found = True
        break

if not found:
    print("no PASS marker found")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
no PASS marker found

Correct, and the loop's actual work is two lines of the seven. The rest exists only to carry one bit of information — did we break? — past the end of the loop.

2. The Loop else

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lines = ["INFO start", "INFO end"]

for line in lines:
    if "PASS" in line:
        print("found:", line)
        break
else:
    print("no PASS marker found")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
no PASS marker found

Same output. The flag is gone, along with the declaration and the check.

And when the search succeeds, the else does not run:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lines = ["INFO start", "PASS smoke", "INFO end"]

for line in lines:
    if "PASS" in line:
        print("found:", line)
        break
else:
    print("no PASS marker found")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
found: PASS smoke

3. The Rule

The else block runs when the loop finished without executing a break.

That is the entire definition, and three things follow from it that are not obvious:

SituationDoes else run?Why
the loop hit a breaknoa break happened
the loop ran out of itemsyesno break happened
the collection was emptyyeszero passes, so no break happened
a continue was usedyescontinue is not break
the loop contains no break at allyes, alwaysthere was never a break to happen

Note the indentation: else aligns with for, not with the if inside the body. That alignment is what tells Python — and the reader — which statement it belongs to. Chapter 9.2's rule, applied to a different pair.

4. The Keyword Is a Bad Name

Here is the misreading everyone starts with: that the else pairs with the if inside the loop, and runs on passes where the condition was false. It does not. It is not inside the loop at all; it runs at most once, after the loop is over.

The second misreading is that else means otherwise in the ordinary sense. It does not mean that either.

Read it as nobreak:. That is what it means, and that word was seriously proposed when the feature was designed. Substituting it mentally makes every example in this chapter read correctly on the first pass:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for line in lines:
    ...
    break
nobreak:                 # <- how to read `else:` here
    print("not found")

5. continue Does Not Suppress It

This is the case that catches people who have understood everything else:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for n in [1, 2, 3]:
    if n == 2:
        continue
    print("n =", n)
else:
    print("else still ran — continue is not break")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
n = 1
n = 3
else still ran — continue is not break

continue skipped a pass; it did not leave the loop. The rule is about break specifically, and nothing else in the language suppresses the else.

6. An Empty Collection Still Runs It

Zero passes means zero opportunities to break, so the else runs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for line in []:
    if True:
        break
else:
    print("else ran: zero passes, so no break happened")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
else ran: zero passes, so no break happened

This is usually what you want — searching an empty log should report not found — but it is worth knowing it is the behaviour, because "we never looked" and "we looked and found nothing" arrive at the same branch.

7. An else With No break Is Pointless

If the loop contains no break anywhere, the else always runs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for n in [1, 2]:
    print(n)
else:
    print("else always runs when the loop contains no break")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1
2
else always runs when the loop contains no break

That is identical to writing the line after the loop with no else at all — and worse, because a reader has to check the whole body for a break before concluding it is unconditional.

8. It Works on while Too

Same rule — the else runs when the loop ended by its condition going false rather than by a break:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
attempts = 0

while attempts < 3:
    attempts += 1
    print("attempt", attempts)
else:
    print("while-else: ran out of attempts without breaking")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
attempt 1
attempt 2
attempt 3
while-else: ran out of attempts without breaking
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
attempts = 0

while attempts < 3:
    attempts += 1
    if attempts == 2:
        print("breaking at", attempts)
        break
else:
    print("this does not print")

print("after")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
breaking at 2
after

For a bounded retry this is exactly the shape Chapter 10.5 wanted: the break is the success exit, and the else is the bound was reached and it never worked — stated once, in the right place, with no flag.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
attempt_results = ["timeout", "timeout", "timeout"]

for attempt in range(3):
    result = attempt_results[attempt]
    print("attempt", attempt + 1, "->", result)
    if result == "ok":
        print("succeeded on attempt", attempt + 1)
        break
else:
    print("gave up after 3 attempts")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
attempt 1 -> timeout
attempt 2 -> timeout
attempt 3 -> timeout
gave up after 3 attempts

Note what changed from Chapter 10.5: the retry is now a for over range(3) rather than a while with a counter, because break supplies the early exit that was missing in Chapter 10.6 Section 4. The bound is the range; the success exit is the break; the give-up message is the else.

9. Should You Use It?

An honest verdict, because this is a construct on which competent engineers disagree.

In favour: it removes a flag, it puts the not-found message adjacent to the search rather than three lines below it, and it is impossible to get the flag logic wrong because there is no flag.

Against: the keyword says the opposite of what it does, every reader who has not met it will misread it once, and some teams ban it outright in review.

The practical position. Use it where the pattern is unmistakable — a short search loop whose break is visible in the same screenful. Avoid it in a long body where the break is far from the else, because then the reader has to search for the thing the clause depends on. And know it regardless, because you will read it.

The flag version is never wrong:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
lines = ["INFO start", "INFO end"]

found = False

for line in lines:
    if "PASS" in line:
        found = True
        break

if found:
    print("found a PASS marker")
else:
    print("no PASS marker found")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
no PASS marker found

It is longer, and it is immediately readable by everyone. That is a real argument.

10. Predict, Run, Observe

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# A
for n in [1, 2, 3]:
    if n == 5:
        break
else:
    print("A: else ran")

# B
for n in [1, 2, 3]:
    if n == 2:
        break
else:
    print("B: else ran")
print("B: after")

# C
for n in []:
    print("C: body")
else:
    print("C: else ran")

# D
found = ""
for name in ["clk", "rst", "data"]:
    if name.startswith("r"):
        found = name
        break
else:
    found = "none"
print("D:", found)

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
A: else ran
B: after
C: else ran
D: rst

A — nothing equals 5, so no break, so the else runs.

B — the break at 2 suppresses the else; only the line after the loop prints.

C — zero passes, no break, else runs. The body never did.

D — "rst" matches, so the loop breaks and found keeps the name. Had no name started with r, the else would have set it to "none".

11. Exercises

Exercise 1 — delete the flag. Rewrite Section 1's program using a loop else and confirm the output is unchanged. Count the lines removed.

Exercise 2 — predict the four cases. For a loop with a break inside, say whether the else runs when: (a) the break is reached (b) the collection is exhausted (c) the collection is empty (d) a continue ran on every pass. Verify all four.

Exercise 3 — the misreading. Write down, in one sentence, what you would have guessed else meant before reading Section 4. Then write the correct one-sentence rule.

Exercise 4 — find the first gap. Given seeds = [4210, 4211, 4213, 4214], find the first position where the seed is not one more than the previous, and report it. If the seeds are consecutive throughout, report that instead — using a loop else.

Exercise 5 — the pointless else. Write a loop with an else and no break. Run it, then delete the else and unindent its body. Explain in one sentence why the two programs are the same and which is better.

Exercise 6 — retry with for. Rewrite the bounded retry from Chapter 10.5 as a for over range(4) with a break and an else, using attempt_results = ["timeout", "ok", "timeout", "timeout"]. Print which attempt succeeded.

Exercise 7 — while-else. Write a while loop that drains a queue of three jobs, with an else that prints queue emptied cleanly. Then add a break that triggers on a job named "bad" and verify the else is skipped.

Exercise 8 — argue both sides. In two sentences each, make the case for the loop else and the case for the flag. Then say which you would use in a 40-line loop body and why.

12. Summary

  • A loop may carry an else, and it runs when the loop finished without executing a break.
  • It pairs with the loop, not with the if inside it — the alignment with for or while is what says so.
  • Read it as nobreak:. The keyword is widely agreed to be a poor name, and substituting the better one makes every example read correctly.
  • A continue does not suppress it. The rule is about break only.
  • An empty collection still runs it — zero passes means no break happened.
  • An else on a loop with no break always runs, which makes it identical to code after the loop and actively misleading. Delete it.
  • It works on while as well as for, where it means the condition went false rather than the loop breaking out.
  • A bounded retry is its best use: range() is the bound, break is the success exit, else is gave up.
  • The flag version is never wrong, and is more widely readable. Use the else where the break is visible nearby.

The next chapter removes two more pieces of manual bookkeeping from loops.

Where this fits

Part of the Python curriculum.