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.
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 hereShow 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.