Why this matters
Computers are valuable because they repeat work tirelessly: process every line of a file, retry a failed connection, keep asking until the user gives valid input, run a game until the player quits. A loop runs a block of code many times. Python has two loop statements; while is the more fundamental one: it repeats as long as a condition stays true. It is the right tool whenever you do not know in advance how many repetitions you need.
The while statement
count = 1
while count <= 3:
print("iteration", count)
count += 1
print("done")
iteration 1
iteration 2
iteration 3
done
while looks like if: a condition, a colon, an indented block. The difference is what happens after the block. An if continues with the next statement; a while goes back and tests the condition again. Each pass through the block is called an iteration.
+-------------------+
v |
condition true? --yes--> block
| no
v
next statement after the loop
Underneath, this is exactly what the bytecode does: test the condition, jump past the block if false, otherwise run the block and jump back to the test.
Tracing a loop
The best way to understand a loop is to trace it by hand, writing down the variables at each test:
| test # | count | count <= 3 | action |
|---|---|---|---|
| 1 | 1 | True | print 1, count becomes 2 |
| 2 | 2 | True | print 2, count becomes 3 |
| 3 | 3 | True | print 3, count becomes 4 |
| 4 | 4 | False | leave the loop |
Note that the condition is tested one more time than the body runs, and if it is false at the start, the body runs zero times.
The parts of a counting loop
Most loops that count have three parts. Forget any one and the loop misbehaves:
i = 0 # 1. initialize the loop variable
while i < 5: # 2. test it
print(i)
i += 1 # 3. update it, moving towards the end
i += 1 is augmented assignment, short for i = i + 1 (from the expressions module). Remember that for an int it creates a new int object and rebinds i; ints are immutable.
Accumulators
A common pattern keeps a running result in a variable, called an accumulator, updated on each iteration:
n = int(input())
total = 0
i = 1
while i <= n:
total += i
i += 1
print(f"1 + ... + {n} = {total}")
With input 100:
1 + ... + 100 = 5050
The accumulator is initialized before the loop to the "empty" value: 0 for a sum, 1 for a product, "" for a string you build up.
Loops that depend on data
A while loop shines when the number of iterations depends on the data. This loop counts how many times a number can be halved before it reaches 1:
n = int(input())
halvings = 0
while n > 1:
n //= 2
halvings += 1
print(halvings)
For 1000 it prints 9. Nobody had to know the answer in advance; the loop stops when the condition says so.
Sentinel-controlled input
Programs often read input until a special sentinel value says "stop". The classic structure reads once before the loop and again at the end of each iteration:
total = 0
line = input()
while line != "done":
total += int(line)
line = input()
print(f"total: {total}")
With input lines 4, 10, done:
total: 14
Trace it: read 4, test (not done), add, read 10, test, add, read done, test fails, print. The "priming read" before the loop makes sure the condition always has a fresh value to test.
while True with break
The same loop is often written with an infinite loop that exits from the middle, using break (covered fully two lessons from now). break leaves the loop immediately:
total = 0
while True:
line = input()
if line == "done":
break
total += int(line)
print(f"total: {total}")
This avoids writing input() twice. Both styles are common; choose the one that reads more clearly.
Reading until the input ends
When input comes from a file or pipe, there may be no sentinel; it just ends. input() then raises EOFError. You will learn to handle that with try/except in the errors module, and to read all of stdin with sys.stdin in the input and output lesson.
Validating input in a loop
Loops let a program insist on good input instead of crashing or giving up:
age = int(input())
while age < 0 or age > 150:
print("Please enter an age between 0 and 150.")
age = int(input())
print(f"Age accepted: {age}")
The loop body runs only while the input is bad, so valid input on the first try skips it entirely.
Infinite loops
If the condition never becomes false, the loop runs forever. That is sometimes intended (a server waiting for requests, while True: with a break), but usually it is a bug:
i = 0
while i < 5:
print(i)
# forgot i += 1: prints 0 forever
In a terminal, stop a runaway program with Ctrl-C. The terminal sends the signal SIGINT to the process, and Python turns it into a KeyboardInterrupt exception, which stops the program with a traceback:
^CTraceback (most recent call last):
File "main.py", line 3, in <module>
print(i)
KeyboardInterrupt
In learnprog, a program that runs longer than 10 seconds is stopped and reported as "timed out: endless loop, or waiting for input?". When you see that, check that every path through your loop body moves the loop variable toward the end, and that you are not calling input() more often than there are input lines.
Common mistakes
- Forgetting the update. No
i += 1means an infinite loop. - Updating in the wrong direction.
while i < 10:withi -= 1never ends. - Off-by-one.
while i < nandwhile i <= ndiffer by one iteration. Trace the first and last iterations to check which you need. - Initializing the accumulator inside the loop.
total = 0inside the body resets it every time, so you only get the last value. - Testing a stale value. In a sentinel loop, forgetting to read the next value at the end of the body tests the same value forever.
- Exact float comparisons.
x = 0.0andwhile x != 1.0: x += 0.1never stops, because0.1cannot be represented exactly andxskips past 1.0. Use<with floats.
Recap
while condition:repeats its block as long as the condition is truthy, testing before every iteration; the body may run zero times.- Counting loops need initialization, a test and an update; accumulators start at an "empty" value before the loop.
- Sentinel loops read before the loop and at the end of each iteration, or use
while True:withbreak. - An infinite loop never ends; Ctrl-C raises
KeyboardInterrupt, and learnprog reports a time-out. - Trace loops with a table of variable values when in doubt.
Going further: run import dis; dis.dis("while i < 3:\n i += 1") and find the backward jump (JUMP_BACKWARD) that makes it a loop. Then look up the Collatz conjecture: a three-line loop that mathematicians still cannot prove always stops.