Post
Loops in Python: for and while
Running code again and again: for over a list or a range, enumerate and zip, while, break and continue, the else clause on a loop, and the loop bugs everyone writes once.
A loop runs the same block of code again and again: once for every item in a list, once for every number in a range, or for as long as a condition is true.
Every example ran with Python 3.13, checked against the control flow tutorial and the for statement reference.
for: one item at a time
The tutorial says for “iterates over the items of any sequence (a list or a string), in the order that they appear in the sequence”. Each time around, the loop variable holds the next item:
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit, len(fruit))
print("done")
apple 5
banana 6
cherry 6
done
The indented block is the body of the loop. print("done") isn’t indented, so it runs once, after the loop. A string is a sequence of characters, so a loop over a string gets one character at a time:
for ch in "hey":
print(ch.upper())
H
E
Y
A common pattern is the accumulator: a variable that starts empty and grows on each pass:
scores = [90, 72, 85, 64]
total = 0
passed = []
for score in scores:
total += score
if score >= 70:
passed.append(score)
print(total, passed)
311 [90, 72, 85]
range(): looping over numbers
range() gives a sequence of whole numbers. With one argument it starts at 0 and stops before the end, as a slice does. With two, you choose the start. A third argument is the step:
print(list(range(5)))
print(list(range(2, 6)))
print(list(range(10, 0, -3)))
for i in range(3):
print("attempt", i + 1)
[0, 1, 2, 3, 4]
[2, 3, 4, 5]
[10, 7, 4, 1]
attempt 1
attempt 2
attempt 3
enumerate and zip
For the position as well as the item, use enumerate() rather than range(len(items)). It gives pairs of index and item, and start sets where counting begins:
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")
1. apple
2. banana
3. cherry
i, fruit unpacks each pair, the same way x, y = point unpacks a tuple. To go through two lists side by side, use zip():
names = ["Dana", "Avi", "Noa"]
ages = [31, 25, 40]
for name, age in zip(names, ages):
print(name, age)
Dana 31
Avi 25
Noa 40
zip() stops at the end of the shortest list. Since Python 3.10, zip(names, ages, strict=True) raises a ValueError instead when the lengths differ.
while: loop until a condition changes
while checks its condition before every pass and repeats while it is true, so it fits when you don’t know the number of passes in advance:
balance = 100
years = 0
while balance < 200:
balance *= 1.1
years += 1
print(years, round(balance, 2))
8 214.36
Something in the body has to change the condition, or the loop never ends. In a terminal, Ctrl+C stops a stuck program.
break and continue
break leaves the loop right away. continue skips the rest of the current pass and moves on to the next one:
for n in [4, 7, -1, 9, 0, 3]:
if n < 0:
continue
if n == 0:
break
print(n)
4
7
9
3 is never reached because break ends the loop at 0. Both work in while loops too.
A loop that stops in the middle usually takes the shape while True plus break, as in “keep asking until the answer is valid”:
attempts = ["", "abc", "42"]
while True:
answer = attempts.pop(0)
if answer.isdigit():
break
print("not a number:", repr(answer))
print("got", int(answer))
not a number: ''
not a number: 'abc'
got 42
In a real program, answer = input("Number: ") would take the place of attempts.pop(0).
else on a loop
A loop can have an else block. It runs when the loop finishes normally, and is skipped when the loop ends with break. It fits searches:
for n in [3, 9, 15]:
if n % 2 == 0:
print("found an even number:", n)
break
else:
print("no even numbers")
no even numbers
Read the else as “no break”. Many Python programmers find it confusing, so use it when it shortens the code, and a flag variable when it doesn’t.
Common mistakes
- Changing a list while looping over it. Removing items from the list you’re looping over skips items. Loop over a copy (
for x in items[:]) or build a new list. - Expecting
forto make a new scope. The loop variable still exists after the loop, with its last value. The code blocks post explains why.
What’s next
Next in the series: dictionaries, which store values by name instead of by position, and which you’ll loop over constantly.