Why write a Python `while True:` loop with an explicit break instead of a real condition?
answer
- Where does the tested value come from?
- A top test would duplicate the read
- Read, test, then use
- The loop-and-a-half pattern
- One reachable break, placed high
basics
~20 sBecause the stop test needs a value the body reads. A real condition forces you to duplicate that read above the loop and again at the bottom; while True with one break keeps it in one place.
solid answer
~50 sThis is the loop-and-a-half: the value the condition needs is produced *inside* the body, so a top-tested loop would have to read it once before the loop and again at the end of each pass, leaving two copies to drift apart. `while True:` with a single, clearly-placed `break` keeps the read in one place and puts the stop test exactly where the information first exists. The cost is that the constant-true condition tells the reader nothing, so the discipline matters: **one** exit if you can manage it, placed near the top of the body, with the sentinel test obvious. Choose the sentinel so it cannot appear in real data — a file object's `readline()` returns `""` only at end of file, since a blank line comes back as `"\n"`, whereas `None` is a poor sentinel whenever `None` is a legal value.
code
python · 8 linesimport io
stream = io.StringIO("alpha\nbeta\nSTOP\ngamma\n")
while True:
line = stream.readline()
if not line or line.strip() == "STOP":
break
print(line.strip())go deeper
Recognise the pattern and know its name: a body-first loop written as while True: with a break, used because Python has no do/while and the stop test needs a value the body reads.
Explain the duplicated-read problem concretely, and justify a sentinel choice — why the empty string terminates a readline() loop but None fails when None is legal data.
Show the review discipline: one reachable break placed near the top, no parallel flag, and a stated reason the tested value must reach the sentinel. Be able to argue when the idiom is wrong.
Own the team convention for unbounded loops in long-lived processes — where while True: is the honest statement of intent versus where it hides a termination condition nobody can state.
### The problem the idiom solves A `while` tests its condition at the top of the pass. That is fine when the value being tested already exists, and awkward when the loop's job is to *produce* that value. Reading records until a terminator arrives is the archetype: you cannot test the record before you have read it, and you do not want to process the terminator. Written as a top-tested loop, it comes out like this: ```python line = stream.readline() # read #1 while line and line.strip() != "STOP": print(line.strip()) line = stream.readline() # read #2, must stay identical ``` The body-and-a-half is visible: the read appears twice. Every future change — a different call, an extra strip, an added decode — must be made in both places, and the day the two drift is the day the first record is processed differently from the rest. This is the shape the idiom exists to remove. ### The loop-and-a-half ```python while True: line = stream.readline() if not line or line.strip() == "STOP": break print(line.strip()) ``` Now the read exists once, the stop test sits immediately after it — at the first moment the information exists — and the processing sits after the test. The structure is *read, test, use*, which is also the order the reader thinks in. This is a sentinel-controlled loop: the loop runs until a distinguished value arrives in the data stream, and that sentinel value is consumed by the test rather than processed as data. ### Choosing the sentinel The sentinel must be a value that **cannot occur as real data**, or the loop stops early on a legitimate record. Python's I/O layer is careful about this: a text file object's `readline()` returns the empty string only at end of file, because a genuinely blank line comes back as `"\n"`. The empty string is therefore a safe terminator; `"\n"` would not be. The usual mistake is reaching for `None` when `None` is itself a legal value. A lookup that returns `None` both for "missing" and for "stored as None" cannot terminate a sentinel loop correctly. The fix is a private sentinel object that nothing else can produce: ```python MISSING = object() while True: item = mapping.get(next_key, MISSING) if item is MISSING: break ... ``` `object()` instances are unique and compared with `is`, so no caller-supplied value can be mistaken for the terminator. ### The price, and the discipline that pays it `while True:` moves the termination argument out of the condition and into the body, where a reader has to go looking for it. That is a real loss of local reasoning, and it is why style guidance treats the idiom as a tool rather than a default. Things worth insisting on in review: - **One break, if the loop can be written with one.** Several scattered exits recreate the tangle the idiom was supposed to remove, and make it hard to say what is true when the loop finishes. - **The break near the top.** Read, test, then use. An exit buried thirty lines down is invisible. - **A reachable break.** `while True:` with a `break` on a branch that cannot be taken is an infinite loop with camouflage. Ask what makes the tested value eventually reach the sentinel. - **No redundant flag.** Setting `done = True` *and* breaking, or looping on `while not done:` and also breaking, gives two mechanisms for one decision and lets them disagree. - **A short body.** If the body is long enough that the exit is hard to find, extracting the work into a function usually turns the loop back into something a plain condition can express. ### Alternatives worth knowing A sentinel loop over a repeated zero-argument call has a direct `for` spelling: the two-argument form of `iter()` builds an iterator that calls the function until its result equals the sentinel, so `for line in iter(stream.readline, ""):` replaces the whole `while True` block, and the terminator is stated in the header where a reader expects it. Where the loop is genuinely unbounded — a REPL, a state machine, a supervisor driving until an operator stops it — `while True:` is not a workaround at all but the honest statement of intent, and dressing it up in a flag variable only obscures that. One last note on style: `while True:` and `while 1:` compile to the same thing, and `True` is a keyword in Python 3, so there is no name lookup to save. Write `while True:`; there is no performance argument on the other side.
- What makes a good sentinel value for a Python sentinel-controlled loop?One that cannot appear as real data. A text file object's `readline()` returns `""` only at end of file — a blank line is `"\n"` — so the empty string is safe. `None` is a poor choice whenever `None` is a legal value; use a module-level `object()` instance compared with `is`, which nothing else can produce.
- How do you keep a `while True:` loop from becoming an accidental infinite loop in review?Ask what makes the tested value eventually reach the sentinel, and check that every path through the body moves toward it. Insist on one reachable `break` placed near the top, no parallel flag variable duplicating it, and a body short enough that the exit is visible without scrolling.
- Is there a difference between `while True:` and `while 1:` in Python 3?Not in behaviour or speed. `True` is a keyword, so there is no global lookup to avoid, and the compiler treats both as a constant-true condition. `while True:` is the readable spelling and the one style guides expect; `while 1:` is a habit carried over from Python 2.
saying these in an interview costs you the question
- Says `while True:` is always bad style
- Duplicates the read above the loop and lets copies drift
- Uses `None` as a sentinel when `None` is valid data
- Adds a done flag that duplicates the break
- Writes `while True:` with no reachable break
- Claims `while 1:` is faster than `while True:`