Python has no labelled break — how do you exit two nested loops at once?
answer
- break only leaves one level
- which statement unwinds every loop?
- give the search a name
- a flag the outer loop tests
- itertools.product flattens two loops into one
basics
~20 sbreak leaves only the loop it sits in. Escape both by moving the pair into a function and using return, by setting a flag the outer loop tests, or by flattening the nesting into one itertools.product loop.
solid answer
~50 sPython deliberately has no labelled `break`, so a `break` in the inner loop ends the inner loop and the outer one calmly continues with its next item — the classic "my search kept running after it found the match" bug. Three fixes are idiomatic. The best is to extract the nested loops into a function and `return` the find, since `return` unwinds every loop at once and gives the search a name. The second is a flag: set `found = True`, `break` the inner loop, and immediately test the flag in the outer body to `break` again. The third is to flatten the nesting with `itertools.product`, which turns two loops into one so a single `break` exits everything. Raising a custom exception also works but uses the exception machinery for ordinary control flow, so keep it for cases nothing else reaches.
code
python · 9 linesdef first_drifting_row(batch, checks):
for row_id, amount in batch:
for check in checks:
if round(amount, 2) != amount:
return row_id, check
return None
print(first_drifting_row([(101, 10.0), (102, 20.005)], ("cents", "half-cent")))go deeper
Know that break leaves exactly one loop, and that putting the loops in a function and returning the match is the normal way out of both. Recognising the bug in someone else's nested search is enough here.
Be ready to write all three escapes on a whiteboard — return, a flag tested by the outer loop, and an itertools.product flattening — and say which you would pick for a given shape of search.
Show the tradeoffs: what product materialises, where the flag pattern duplicates the exit condition, and why deep nesting that needs a multi-level escape is a signal to restructure rather than to add machinery.
Frame it as design: treat a loop nest deep enough to need an escape as a missing abstraction, and set the team's line between an extracted, tested search helper and clever flattening that hides the cost of its inputs.
`break` takes no argument and Python has no loop labels, so it always ends the innermost loop that lexically encloses it. In two nested loops the inner `break` therefore returns control to the *outer* loop body, which then carries on to its next item. Concretely: a webhook receiver reconciling a 6,800-row batch scans each row against several rounding checks, breaks the moment it sees a floating-point drift, and then — because only the inner loop stopped — happily scans the remaining 6,700 rows, reporting the last drift it found instead of the first. The code looks right, so this is a favourite interview probe. ### 1. Extract a function and `return` The idiomatic fix. `return` leaves the whole call frame, so every enclosing loop dies with it, no matter how deep. It also forces you to name the search (`first_drifting_row`), gives it one obvious result type, and makes it independently testable. If the caller needs to distinguish "found nothing" from a real find, return `None` or a sentinel and let the caller decide. This is the answer to give first. The other techniques are what you reach for when extraction genuinely is not available — for example when the loop body mutates several local variables the caller needs afterwards. ### 2. A flag the outer loop tests Set a boolean before the loops, set it inside the inner loop just before `break`, and test it in the outer body immediately after the inner loop ends: ```python found = None for row in batch: for check in checks: if drifts(row, check): found = (row, check) break if found: break ``` It is explicit and works at any depth, but it costs an extra variable and an extra test per outer pass, and at three levels the pattern becomes noise. Its real weakness is that the exit condition now lives in two places, so a later edit can change one and not the other. ### 3. Flatten with `itertools.product` `itertools.product(a, b)` yields every pair from the two inputs in the same order the nested loops would visit them, so two loops collapse into one — and one loop needs only one `break`. Unpacking in the `for` target keeps the body identical to the nested version. This reads well for a pure cartesian search over small inputs. It is not free. `product` converts each input argument into a tuple up front and keeps it for the whole traversal, so an unbounded inner iterable is ruled out and memory grows with the inputs. Flattening also removes the natural home for per-outer-item work: if the outer loop opened a resource once per row, there is no longer a place to open and close it. And the inner sequence can no longer depend on the outer item — `product` takes fixed inputs, so a genuinely dependent inner range still needs real nesting. ### 4. Raise and catch Define a small exception class, raise it from the innermost body, and catch it just outside the outer loop. This escapes arbitrary depth, including out of a helper the inner loop called, which is the one thing the other techniques cannot do. The costs are real: it uses exceptions for ordinary control flow, the escape path is far slower than a `break`, and any broad `except Exception` sitting between the raise and the catch will swallow it. Reserve it for deep or callback-driven nesting. ### What not to do Do not reach for `sys.exit()` or `os._exit()` to leave a search — the first raises `SystemExit` through your callers, the second kills the process without running cleanup. Do not use `continue` in the outer loop expecting it to abandon the outer pass *and* the inner loop; the inner loop has already ended by the time the outer body resumes. And do not add a `while True` wrapper purely so there is something to break out of. ### Why the language has no labels The omission is deliberate: several proposals for labelled break have been rejected on the grounds that code needing them is usually one refactor away from being clearer. That framing is the answer an interviewer is hoping for — deeply nested loops with a multi-level escape are a design smell, and "extract a function" fixes the nesting and the exit in one move. ### Judging the answers A strong answer names `return` first, mentions the flag as the local fallback, knows that `itertools.product` flattens rather than escapes, and can say what each costs. A weak answer looks for Java-style labels and, not finding them, concludes Python cannot do it.
- What does `itertools.product` cost that a hand-written nested loop does not?It converts each input argument into a tuple before yielding anything and holds those tuples for the whole traversal, so an unbounded inner iterable is impossible and memory grows with the inputs rather than with the output. Flattening also destroys the per-outer-item boundary, so work that ran once per outer value — opening a resource, resetting an accumulator — no longer has a place to live, and the inner sequence can no longer depend on the current outer item.
- Why is raising a custom exception to escape nested loops usually the last resort?It uses the exception machinery for ordinary control flow: the escape path is much slower than a `break`, the intent is hidden from a reader scanning the loops, and any broad `except Exception` between the raise and the catch will swallow it. Its one genuine advantage is depth — it escapes out of helper functions the inner loop called, which `break` and a flag cannot do — so keep it for that case.
- How would you break out of the outer loop only, letting the inner one finish first?There is no statement for it. The inner loop must end naturally, and then the outer body decides — typically by testing a verdict the inner loop computed. The clean version extracts the inner loop into a function that returns that verdict, so the outer body reads as a single `if` followed by `break` rather than as flag bookkeeping spread across two suites.
saying these in an interview costs you the question
- Hunts for a labelled break like Java's
- Thinks one break leaves both nested loops
- Says continue in the outer loop escapes both
- Reaches for sys.exit to leave a search loop
- Wraps the search in except Exception as control flow
- Assumes itertools.product streams its inputs lazily