while Loops

while loops repeat a block for as long as a condition stays truthy, checked before every iteration — the right tool when you don't know in advance how many times you'll need to loop.

Where a for loop iterates over a known sequence of items, a while loop repeats based purely on a condition, re-evaluated before each pass through the body. This makes while the natural choice whenever the number of iterations depends on runtime behavior — waiting for user input, polling until a state changes, or processing a stream of unknown length — rather than walking a fixed collection.

Basic Syntax and Pre-Test Behavior

A while statement is while <condition>: followed by an indented block. Before *every* iteration — including the very first — Python evaluates the condition; if it is falsy, the loop body never runs at all. This is called pre-test (or 'entry-controlled') looping: unlike some languages' do...while, Python has no built-in post-test loop that guarantees at least one execution.

To simulate a do-while (run at least once, then check), structure it as an infinite loop with the condition check and a break at the end of the body — see the pattern below.

count = 0
while count < 3:
    print(count)
    count += 1
# 0
# 1
# 2

# Pre-test in action: body never runs if the condition starts False
while False:
    print('never printed')

The Condition Must Change: Loop Progress

For a while loop to ever terminate, something evaluated by the condition must change on each iteration, and that change must eventually make the condition falsy. This is sometimes called making 'loop progress'. Forgetting to update the controlling state is the single most common while-loop bug.

# Correct: 'count' changes every iteration, guaranteeing termination
count = 5
while count > 0:
    print(count)
    count -= 1     # progress toward the terminating condition

Infinite Loops: Intentional and Accidental

while True: creates a loop with no automatic exit — deliberately used when the exit condition is more naturally expressed inside the loop body (e.g. 'keep looping until we decide to stop', checked partway through processing). It must be paired with an explicit break (or return, or an exception) somewhere inside, or the program will hang.

An *accidental* infinite loop — where the condition never becomes false due to a bug — is one of the most common beginner mistakes. Always double-check that the loop body actually modifies whatever the condition depends on.

while True:
    response = input('Enter a positive number (or "q" to quit): ')
    if response.lower() == 'q':
        break
    if response.isdigit() and int(response) > 0:
        print(f'You entered {response}')
        break
    print('Invalid input, try again.')

Sentinel-Controlled Loops

A sentinel value is a special marker that signals 'stop looping' — commonly used when reading input or processing a stream until a specific terminating value appears (e.g. an empty string, a specific command like 'quit', or a sentinel object like None). The loop condition directly tests for the sentinel.

Notice the input-then-check pattern repeats the input() call both before the loop and at the end of its body — this is a natural consequence of while being pre-test: the first value must exist before the condition can be checked at all.

total = 0
entry = input('Enter a number (blank to finish): ')
while entry != '':
    total += int(entry)
    entry = input('Enter a number (blank to finish): ')
print(f'Total: {total}')

Simulating a do-while Loop

When you genuinely need the body to run at least once regardless of the condition — the classic 'do the thing, then check whether to repeat' shape — wrap it in while True: and put the condition check (with a break) at the end of the body instead of the top.

while True:
    guess = int(input('Guess the number (1-10): '))
    if guess == 7:
        print('Correct!')
        break
    print('Try again.')
    # loop repeats -- the condition ('correct?') is checked at the *end* of each pass

while with else

Like for, a while loop can have an else clause, which runs only if the loop finished by the condition becoming falsy naturally (not via break). This is covered in detail in 'else on Loops' — briefly here: it's most useful for search-until-found patterns.

n = 17
i = 2
while i * i <= n:
    if n % i == 0:
        print(f'{n} is divisible by {i}')
        break
    i += 1
else:
    print(f'{n} is prime')   # runs because the loop completed without break

Nested while Loops and Combining with for

while loops can nest inside other while or for loops just like any other block. Each loop's exit condition (or break) is independent — break only exits the innermost enclosing loop.

row = 0
while row < 3:
    col = 0
    while col < 3:
        print(f'({row},{col})', end=' ')
        col += 1
    print()
    row += 1

for vs. while: Choosing the Right Tool

Use for when iterating over a known, finite collection or a computable range — it's shorter, avoids off-by-one and forgotten-increment bugs, and directly expresses 'do this for each item'. Use while when the number of iterations is not known ahead of time and depends on a condition evaluated at runtime — waiting on external state, retry logic, or processing until a signal is received.

# for: iterating a known collection -- idiomatic
for item in shopping_list:
    print(item)

# while: condition-driven, unknown iteration count -- idiomatic
import random
attempts = 0
while random.random() > 0.1:   # keep going until a ~10% chance event happens
    attempts += 1
print(f'Took {attempts} attempts')

Edge Cases and Practical Considerations

A condition that references a mutable object's state (like list length) rather than a simple counter needs the same care: make sure the mutation genuinely happens on the path the loop takes, including every branch. Also watch for conditions built on floating-point arithmetic, where accumulated rounding error can cause a loop to run one iteration more or fewer than expected.

# Fragile: floating point step accumulation
x = 0.0
while x != 1.0:      # may never become exactly 1.0 due to rounding
    x += 0.1
    # could loop far longer than intended, or forever

# Safer: use an integer counter and compute the float value from it
steps = 10
for i in range(steps + 1):
    x = i / steps
    print(round(x, 2))

Key Takeaways

while is pre-test: the condition is checked before every iteration, including the first — the body may run zero times.

Something the condition depends on must change each iteration ('loop progress'), or the loop never terminates.

while True: paired with an internal break is the standard way to write loops whose natural exit point is mid-body, or to simulate a do-while.

Sentinel values are a common way to signal loop termination when reading input or streaming data.

Prefer for for known/finite iteration; reserve while for condition-driven loops whose length isn't known in advance.

Avoid controlling a loop's exact termination with floating-point equality — accumulated rounding error can cause the condition to never (or unexpectedly) become true.

count = 0 while count < 5: print(count) # forgot to increment count -- infinite loop count = 0 while count < 5: print(count) count += 1

Every while loop needs some state change inside the body that moves the condition toward becoming false. Forgetting the increment/update is the most common cause of an accidental infinite loop.

x = 0.0 while x != 1.0: x += 0.1 for i in range(10): x = (i + 1) / 10

Floating-point addition accumulates rounding error, so x may never land on exactly 1.0. Prefer an integer-driven loop (or a tolerance check) rather than exact float equality as a loop-terminating condition.

while True: process_next_item() # no break anywhere -- runs forever while True: item = get_next_item() if item is None: break process(item)

while True: requires an explicit exit path (break, return, or a raised exception) inside the body. Without one, the loop truly never ends.

What does 'pre-test' mean for a while loop, and what is its consequence for the number of times the body can run?

The condition is evaluated before each iteration, including the very first. If it's falsy from the start, the body executes zero times — Python's while never guarantees at least one run, unlike a do-while loop in some other languages.

Why does a while loop require 'loop progress' to terminate, and what usually causes an infinite loop bug?

Something the condition checks must change each iteration so the condition eventually becomes false. The most common bug is forgetting to update that state (e.g. forgetting count += 1), leaving the condition permanently true.

How would you simulate a do-while loop (run the body at least once) in Python?

Wrap the body in while True: and place the exit condition check with a break at the end of the body instead of the top, so the body always executes at least once before the condition is ever tested.

Give a scenario where while is clearly the better choice over for, and explain why.

Reading user input until a sentinel value (like a blank line or 'quit') is entered — the number of iterations isn't known ahead of time and depends entirely on runtime behavior, which is exactly what while's condition-driven looping is designed for.

Why is while x != 1.0: risky when x is being incremented by 0.1 each time?

Binary floating-point cannot represent 0.1 exactly, so repeated addition accumulates rounding error and x may never equal exactly 1.0, causing the loop to run far longer than intended or forever.

What is a sentinel value, and give one example of using it to control a while loop.

A sentinel is a special marker value that signals 'stop'. Example: reading numbers from input until an empty string is entered — while entry != '': uses the empty string as the sentinel that ends the loop.

What does this print, and what computation is it performing?

120. This computes 5! (factorial): result accumulates 5*4*3*2 = 120, and the loop stops once n is no longer greater than 1.

Write a function collatz_steps(n) that returns how many steps it takes for the Collatz sequence starting at n to reach 1 (n -> n/2 if even, n -> 3n+1 if odd), using a while loop.

def collatz_steps(n: int) -> int: steps = 0 while n != 1: if n % 2 == 0: n //= 2 else: n = 3 * n + 1 steps += 1 return steps print(collatz_steps(6)) # 8

The loop's condition n != 1 is checked before every step; each iteration applies the Collatz rule and counts one step, guaranteeing progress toward the (conjectured) terminating value of 1.

What does this print? (assume familiarity with continue, covered in break/continue/pass)

Prints 0 1 2 3 4 6 7 8 9 -- when i == 5, continue skips the print(i) call for that iteration after incrementing i, so 5 is the only number never printed.

Write a sentinel-controlled while loop that reads numbers from a predefined list readings = [23, 45, -1, 12] one at a time (simulate input with an index), stopping when it encounters -1, and prints the running total of numbers read before the sentinel.

readings = [23, 45, -1, 12] index = 0 total = 0 while readings[index] != -1: total += readings[index] index += 1 print(f'Total before sentinel: {total}') # 68

-1 acts as the sentinel value; the loop condition checks the current reading before adding it to the total, and stops as soon as -1 is encountered, correctly excluding it from the sum.

n = 5
result = 1
while n > 1:
    result *= n
    n -= 1
print(result)
i = 0
while i < 10:
    if i == 5:
        i += 1
        continue
    print(i)
    i += 1