Loops: for and while
Repeating work
Printing the numbers 1 to 5 by writing five print lines is tedious. Printing 1 to 1000 that way is impossible. A loop runs a block of code repeatedly, and it is the point at which programs start doing things you could not sensibly do by hand.
Python has two loops: for and while. They exist for different situations.
The for loop
A for loop walks through a sequence of items, one at a time, running its block once for each.
subjects = ["Maths", "Physics", "Chemistry", "English"]
for subject in subjects:
print("Studying", subject)
print("Timetable finished")
Read it as: "for each subject in subjects, do this". The variable subject is created by the loop and takes the value of each item in turn. You choose that name; it is not special.
Like if, the header line ends with a colon and the repeated block is indented.
range()
Often you want to repeat something a fixed number of times rather than walk through existing data. range() generates a sequence of integers for exactly that.
for i in range(5):
print("Iteration", i)
print("---")
for i in range(1, 6):
print(i, "squared is", i ** 2)
print("---")
for i in range(0, 21, 5):
print(i)
print("---")
for i in range(5, 0, -1):
print(i)
The three forms:
range(stop)— from 0 up to but not includingstop.range(5)gives 0, 1, 2, 3, 4 — five values.range(start, stop)— fromstartup to but not includingstop.range(start, stop, step)— as above, counting in steps ofstep. A negative step counts downwards.
The excluded upper bound feels odd at first, but it makes range(n) produce exactly n values, which is usually what you want. To loop over 1 to 10 inclusive, write range(1, 11).
total = 0
for i in range(1, 11):
total += i
print("Sum of 1 to 10:", total)
print("Average:", total / 10)
table_of = 7
for i in range(1, 11):
print(f"{table_of} x {i} = {table_of * i}")
The first pattern — set an accumulator to 0 before the loop, add to it inside — is one you will use constantly for totals, counts and running maximums.
The while loop
A while loop repeats as long as a condition stays true. It checks the condition before each pass; when the condition becomes false, the loop ends and execution continues after it.
balance = 1000
year = 0
while balance < 2000:
balance = balance * 1.08
year += 1
print(f"Balance passed 2000 after {year} years")
print(f"Final balance: {balance:.2f}")
You could not easily write this as a for loop, because you do not know in advance how many years it takes — that is the answer you are looking for. This is the defining difference:
- Use
forwhen you know the collection or the number of repetitions up front. - Use
whilewhen you repeat until some condition changes, and the count is not known in advance.
Every while loop needs three things, and missing any one of them causes a bug:
- A variable set up before the loop.
- A condition that uses it.
- Something inside the loop that changes that variable, so the condition can eventually become false.
If nothing inside the loop moves the condition towards false, the loop never stops. while True: with no exit, or forgetting count += 1, will hang your program and keep running until you stop it — Ctrl+C in a terminal, or closing the tab in a browser runner. Before running a while loop, check with your eyes that something inside it changes the condition.
break and continue
Two statements alter a loop from the inside.
break leaves the loop immediately, skipping any remaining passes.
continue skips the rest of the current pass and jumps straight to the next one.
marks = [78, 65, 0, 91, 45, 0, 88]
for m in marks:
if m == 0:
continue
print("Valid mark:", m)
print("---")
for m in marks:
if m > 90:
print("Found a mark above 90:", m)
break
print("Checking", m)
The first loop skips the zeros and processes everything else. The second stops entirely as soon as it finds what it was looking for — useful when you only need the first match and there is no point examining the rest.
break also gives a while True: loop a legitimate exit:
count = 0
while True:
count += 1
if count * count > 500:
break
print(f"{count} is the first number whose square exceeds 500")
print(count * count)
This is a normal pattern, not a hack, but only when the break is unmistakably reachable.
Looping with an index
Sometimes you need the position of an item as well as the item itself. enumerate() gives you both.
subjects = ["Maths", "Physics", "Chemistry"]
for position, subject in enumerate(subjects, start=1):
print(f"{position}. {subject}")
This is cleaner than keeping a separate counter variable and remembering to increment it.
Common mistakes
Off-by-one with range. range(1, 10) stops at 9. For 1 to 10 inclusive, write range(1, 11). When a loop produces one result too few, this is almost always why.
Forgetting to update the while variable.
i = 1
while i <= 5:
print(i)
# i never changes - infinite loop
The i += 1 must be inside the loop body.
Resetting the accumulator inside the loop. Putting total = 0 inside the loop wipes it out on every pass, so the final answer is just the last value. Initialise it before the loop.
Indenting the wrong lines. A line meant to run once per pass, left unindented, runs only after the loop finishes. A line meant to run once, indented, runs every pass. Check what belongs inside.
Modifying a list while looping over it. Removing items from a list inside a for loop over that same list causes skipped elements, because the positions shift underneath you. Build a new list instead.
Using while where for fits. Manually managing a counter with while when you simply want ten repetitions is more code and more places to make a mistake. for i in range(10): cannot loop forever.
Practice
Try the loop exercises on the practice page.