Control flowChapter 46 of 114
For Loops
Walk a sequence, count with range, and loop over dictionaries.
Looping over a sequence
A for loop takes each item in turn. It works on anything iterable — lists, strings, tuples, sets, dictionaries, files, ranges:
for name in ["Ada", "Grace"]:
print(name)
for letter in "hi":
print(letter)Output
Ada Grace h i
There is no index to manage and no length to get wrong.
range
range(n) counts from zero up to but not including n:
for i in range(3):
print(i)Output
0 1 2
With two arguments it is start and stop; with three, a step:
print(list(range(2, 6)))
print(list(range(0, 10, 3)))
print(list(range(3, 0, -1)))Output
[2, 3, 4, 5] [0, 3, 6, 9] [3, 2, 1]
range does not build a list. It produces numbers as they are asked for, which is why range(1_000_000) costs nothing until you loop over it.
print(range(5))
print(list(range(5)))Output
range(0, 5) [0, 1, 2, 3, 4]
The loop variable after the loop
It survives, holding the last value. Relying on that is a smell, but knowing it explains some confusing bugs:
for i in range(3):
pass
print(i)Output
2
Looping over a dictionary
Plain iteration gives keys. items() gives both:
person = {"name": "Ada", "born": 1815}
for key in person:
print(key)
for key, value in person.items():
print(key, value)Output
name born name Ada born 1815
Unpacking in the loop
When the items are pairs, unpack them in the for line:
pairs = [("Ada", 1815), ("Grace", 1906)]
for name, year in pairs:
print(f"{name}: {year}")Output
Ada: 1815 Grace: 1906
Nested loops
The inner loop runs fully on every pass of the outer one:
for row in range(1, 3):
for col in range(1, 4):
print(row, "x", col, "=", row * col)Output
1 x 1 = 1 1 x 2 = 2 1 x 3 = 3 2 x 1 = 2 2 x 2 = 4 2 x 3 = 6
for...else
The else runs if the loop finished without a break. It is the tidy way to express "searched everything and found nothing":
names = ["Ada", "Grace"]
for name in names:
if name == "Alan":
print("found")
break
else:
print("not in the list")Output
not in the list
Without it you need a flag variable, which is more code doing less.
Do not change what you are looping over
Adding or removing items while iterating shifts positions under the loop. Loop over a copy, or build a new list:
numbers = [1, 2, 3, 4]
kept = [n for n in numbers if n % 2 != 0]
print(kept)Output
[1, 3]
Test yourself
2 questionsWhat does range(3, 0, -1) produce?
Show the answer
3, 2, 1 — Start, stop, step. The stop is not included, so it ends at 1.
Why does range(1_000_000) cost almost nothing until you loop over it?
Show the answer
It produces numbers on demand rather than building a list — Printing a range shows range(0, 1000000) rather than its contents, which is the giveaway.
Break and Continue
Leave a loop early, or skip the rest of one pass.