A for loop takes each item of a sequence in turn and runs the block with that
item. Python’s for is not a counter with a limit — it is “for each of these”.
for letter in "cat":
print(letter)
print("---")
for language in ["Python", "Rust", "Go"]:
print(f"I am learning {language}")The loop variable (letter, language) is created by the loop and holds one
item per pass. Name it for what it holds, in the singular.
range()
When you do want to count, range produces the numbers for you.
for i in range(5): # 0, 1, 2, 3, 4 - stop is excluded
print(i, end=" ")
print()
for i in range(2, 6): # start at 2
print(i, end=" ")
print()
for i in range(10, 0, -2): # step backwards by 2
print(i, end=" ")
print()Note end=" " — an extra argument to print that replaces the usual newline,
so the numbers stay on one line.
range(5) gives five numbers starting at zero, which is why Python programmers
count 0, 1, 2, 3, 4. It matches list indices exactly.
Accumulating
The most common loop body in any language: build up a result as you go.
temperatures = [18, 21, 25, 19, 30, 22]
total = 0
hottest = temperatures[0]
for t in temperatures:
total += t
if t > hottest:
hottest = t
print(f"Average: {total / len(temperatures):.1f}")
print(f"Hottest: {hottest}")
# Python has these built in, of course:
print(sum(temperatures), max(temperatures), min(temperatures), len(temperatures))Write the loop version once to understand it, then use sum and max — the
built-ins are faster and clearer.
enumerate and zip
Two functions that remove almost all need for manual index juggling.
names = ["Ada", "Alan", "Grace"]
# enumerate gives you position and value at once.
for position, name in enumerate(names, start=1):
print(f"{position}. {name}")
print("---")
# zip walks two sequences in step.
roles = ["Mathematician", "Logician", "Admiral"]
for name, role in zip(names, roles):
print(f"{name} - {role}")Skip the index when you do not need it
for i in range(len(names)): followed by names[i] is C thinking. In Python,
loop over the items themselves, and use enumerate on the rare occasion you
genuinely need the position too.
Nested loops
A loop inside a loop runs the inner one completely for every pass of the outer.
for row in range(1, 4):
for col in range(1, 4):
product = row * col
print(f"{product:3}", end="")
print(){product:3} pads each number to three columns, which is how you line up a
grid without counting spaces by hand.
Nesting multiplies the work
Two nested loops over 1,000 items each is a million passes. Three is a billion. Before nesting deeply, ask whether a dictionary (Day 3) could do the lookup instead.
Before you write your own
Three small steps, each using only what’s above: read a loop, change one, and repair one.
1. Predict. What are the numbers, and how many are there?
for n in range(2, 6):
print(n, end=" ")
print()
print(len(range(2, 6)))Check your prediction
2 3 4 5
4range(2, 6) starts at 2 and stops before 6. That excluded end is what makes
the count easy: there are 6 - 2 numbers, so four. Say out loud why 6 isn’t
printed — if you can explain it, you’ll stop getting it wrong.
2. Modify. Go back to the Sum, count, and largest example above and add a
count of the days warmer than 20 degrees. Print it as Warm days: 4.
Check your change
temperatures = [18, 21, 25, 19, 30, 22]
warm_days = 0
for t in temperatures:
if t > 20:
warm_days += 1
print(f"Warm days: {warm_days}")Warm days: 4A counter is an accumulator that adds 1 instead of adding the value — and only
when the if says so.
3. Fix. Three purchases came to 42.50 altogether, but this prints 12.0.
purchases = [8.50, 22.00, 12.00]
for amount in purchases:
total = 0
total += amount
print(total)Check your fix
purchases = [8.50, 22.00, 12.00]
total = 0
for amount in purchases:
total += amount
print(total)42.5total = 0 was inside the loop, so it reset on every pass, and only the last
purchase survived. Anything that should last across passes is set up once,
before the loop starts.
A spending summary
A week’s spending is in two lists that line up: items[0] cost costs[0], and
so on. Print each purchase, then the total, then the single biggest purchase.
Your output should be exactly:
coffee: 3.50
lunch: 12.00
bus: 2.75
coffee: 3.50
book: 18.00
Total: 39.75
Biggest: book (18.00)items = ["coffee", "lunch", "bus", "coffee", "book"]
costs = [3.50, 12.00, 2.75, 3.50, 18.00]
# Your code hereHint 1: the idea
One loop can do all three jobs. zip walks the two lists together, so each pass
has an item and its cost. Keep a running total (step 3 above), and remember
the biggest so far the way the temperature example remembers hottest — except
that here you need to remember the item’s name as well as its cost.
Hint 2: a first step
Get the per-purchase lines right first:
for item, cost in zip(items, costs): with print(f"{item}: {cost:.2f}") in
the body. Then set total = 0 above the loop and add to it inside.
Show one solution
items = ["coffee", "lunch", "bus", "coffee", "book"]
costs = [3.50, 12.00, 2.75, 3.50, 18.00]
total = 0
biggest_item = ""
biggest_cost = 0
for item, cost in zip(items, costs):
print(f"{item}: {cost:.2f}")
total += cost
if cost > biggest_cost:
biggest_item = item
biggest_cost = cost
print(f"Total: {total:.2f}")
print(f"Biggest: {biggest_item} ({biggest_cost:.2f})")Two variables for the biggest purchase, updated together, because you need the name to print but the cost to compare. On Day 3 you’ll meet containers that keep a name and its cost together in one value.
:.2f on the total matters even though these prices happen to add up exactly:
with other prices, a float total can come out like Day 1’s 0.1 + 0.2, which
prints as 0.30000000000000004.
Going further: a star triangle
Print a left-aligned triangle of stars, five rows tall:
*
**
***
****
*****Two ways exist: a nested loop, and string multiplication ("*" * 3 is "***").
Try both.
# Your code hereHint 1: the idea
Row 1 has one star, row 2 has two, row 5 has five: the row number is the number of stars. So the outer shape is a loop over the rows 1 to 5.
Hint 2: a first step
for row in range(1, 6): print(row) prints 1 to 5 on separate lines. Now make
each line print that many stars instead of the number — string multiplication
does it in one expression.
Show one solution
# With string multiplication - the Python way:
for row in range(1, 6):
print("*" * row)
print()
# With a nested loop - what the multiplication is doing underneath:
for row in range(1, 6):
for _ in range(row):
print("*", end="")
print()_ is the conventional name for a loop variable you do not use. It is an
ordinary name, but readers take it as “this value is deliberately ignored”.
Day 2 is done
Conditions and loops are the whole of control flow. Everything else — functions, classes, generators — is organisation. You can now express any algorithm, even if the expression is longer than it needs to be.
Tomorrow: the containers that make those loops worth writing.
What you learned
for item in sequence:runs the body once per item.range(stop),range(start, stop)andrange(start, stop, step)generate numbers.- Accumulate with
+=, or usesum,max,minandlen. enumerategives position and item;zipwalks two sequences together.- Nested loops multiply the work — mind the cost.