for Loops & range()

The Course
•
January 6, 2026
•
7 min read

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 each item Python
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.

Three forms of range Python
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.

Sum, count, and largest Python
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.

Index and item together Python
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.

Times table Python
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?

What will this print? Python
for n in range(2, 6):
    print(n, end=" ")
print()
print(len(range(2, 6)))
Check your prediction
2 3 4 5
4

range(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: 4

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

The total that keeps resetting Python
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.5

total = 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.

Exercise

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 here
Hint 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.

Exercise

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 here
Hint 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) and range(start, stop, step) generate numbers.
  • Accumulate with +=, or use sum, max, min and len.
  • enumerate gives position and item; zip walks two sequences together.
  • Nested loops multiply the work — mind the cost.
Last updated on January 6, 2026

Was this article helpful?

Your response is saved on this device.