Repeating work: loops
Computers are extraordinary at one thing in particular: doing the same work over and over without getting bored, tired, or sloppy. A loop is how you ask for that. Instead of writing one instruction ten times — or ten thousand times — you write it once and tell the computer how many times to repeat it. This single idea is what lets a program greet a million users, add up every number in a spreadsheet, or check every pixel in a photo, all with just a few lines of code.
:::note Two words we will use on every line below The loop body is the block of code that gets repeated. One single trip through that body — running it from top to bottom one time — is called an iteration. If a loop runs its body five times, we say it does five iterations. :::
Why bother? Imagine you want to print the numbers 0, 1, 2, 3, 4. Without a loop you would copy-paste five almost-identical lines. That is tedious for five, and impossible for five million. Worse, if you later need to change something, you have to change it in every copy. A loop fixes all of this: you describe the work once, and you describe how many times to do it separately.
# The tedious way — five separate lines, easy to get wrong
print(0)
print(1)
print(2)
print(3)
print(4)
# prints 0, 1, 2, 3, 4 — but imagine doing this a million times
Just like with if, two pieces of punctuation decide what belongs to the loop: the colon (:) at the end of the first line, and the indentation — the spaces at the start of the lines underneath it. Indented lines are inside the loop body and run on every iteration. Lines that go back to the left margin are outside the loop; they run only once, after the loop has finished.
The for loop and range
The most common loop is the for loop. Its most common partner is range, a tool that hands the loop a sequence of numbers to walk through. Start with the simplest form, range(n), where n is a single number.
:::tip The most important rule on this page
range(5) produces the five numbers 0, 1, 2, 3, 4. It starts counting at 0, not 1, and it stops before the number you gave it — so 5 itself is never included. Read that twice: "starts at zero, stops before the end." This single off-by-one rule trips up every beginner. range(5) gives you five numbers, but the last one is 4, not 5.
:::
for i in range(5):
print(i)
# prints 0, then 1, 2, 3, 4 (five lines) — never 5
Let us read that out loud, word by word, because every piece matters. for means "repeat the body once for each value." i is the loop variable — a name we made up to hold "the value we are working on right now." in range(5) says where those values come from. The colon ends the header, and the indented print(i) is the body that repeats.
The loop variable is worth dwelling on, because it is where the magic lives. On the first iteration, Python sets i to the first value (0) and runs the body. On the second iteration, it sets i to the next value (1) and runs the body again. It keeps doing this until the values run out. So the single line print(i) behaves differently each time, because i is a different number each time. Here is the full trace:
- Iteration 1:
iis0→ prints0 - Iteration 2:
iis1→ prints1 - Iteration 3:
iis2→ prints2 - Iteration 4:
iis3→ prints3 - Iteration 5:
iis4→ prints4 - No values left → the loop ends.
range can do more than count from zero. Give it two numbers, range(a, b), and it starts at a and stops before b (same "stop before the end" rule). Give it three, range(a, b, step), and it counts in jumps of step instead of 1.
range(5) # 0, 1, 2, 3, 4 (start at 0, stop before 5)
range(2, 6) # 2, 3, 4, 5 (start at 2, stop before 6)
range(0, 10, 2) # 0, 2, 4, 6, 8 (jump by 2 each time)
range(1, 11) # 1, 2, 3, ..., 10 (covers 1 through 10)
That last line is a handy pattern: to loop over 1 through 10 you write range(1, 11), because the end is excluded. If you want to include 10, you must stop before 11.
Looping over a collection (a preview)
A for loop does not only walk over numbers from range. It can walk over any collection — a group of items held together. You will meet collections in detail in the next lessons (text, lists, dictionaries), but here is a taste so the pattern feels familiar when it arrives. A piece of text is a collection of characters, and a list (written with square brackets) is a collection of values. The loop variable simply takes each item in turn.
for ch in "hi":
print(ch)
# prints h, then i (one character at a time)
for n in [3, 5, 9]:
print(n)
# prints 3, then 5, then 9
Notice the shape is identical to the number version: for <variable> in <something to walk through>: followed by an indented body. Whether the "something" is a range, a string, or a list, the loop does the same job — hand you one item at a time.
The while loop
The second kind of loop is the while loop. A for loop is best when you know how many times to repeat (or you have a collection to walk through). A while loop is best when you want to keep going as long as some condition is true — and you might not know in advance how many iterations that will take. It checks the condition, and if it is True it runs the body; then it checks again, and again, stopping the instant the condition turns False.
count = 0
while count < 3:
print(count)
count = count + 1 # change something each time!
# prints 0, 1, 2 — then count is 3, so 3 < 3 is False and it stops
Trace it carefully. We start with count = 0. Is 0 < 3? Yes → print 0, then bump count to 1. Is 1 < 3? Yes → print 1, bump to 2. Is 2 < 3? Yes → print 2, bump to 3. Is 3 < 3? No → the loop stops without running the body again. The key line is count = count + 1: it nudges count closer to the value that makes the condition False.
:::tip The infinite-loop trap
A while loop only stops when its condition turns False. If you forget to change anything inside the body that affects the condition, the condition stays True forever and your program never ends. This is called an infinite loop, and it is the single most common while-loop bug.
:::
Here is the bug and its fix, side by side. The broken version never changes count, so the condition count < 3 is true on the first check and stays true forever:
# BROKEN — count never changes, so this runs forever
count = 0
while count < 3:
print(count) # prints 0, 0, 0, 0, ... endlessly
# FIXED — we change the very variable the condition checks
count = 0
while count < 3:
print(count)
count = count + 1 # now count climbs: 0 -> 1 -> 2 -> 3, then 3 < 3 is False
Rule of thumb: inside every while loop, make sure something is moving toward making the condition False — and double-check it is the same variable the condition tests. (If you ever do trigger an infinite loop while practicing, you can stop a runaway program by pressing Ctrl+C in the terminal.)
Steering a loop: break and continue
Two keywords let you control a loop from inside its body. break exits the loop immediately — it stops the whole thing, even if there were more iterations left to do. continue is gentler: it skips the rest of the current iteration and jumps straight to the next one.
for i in range(5):
if i == 3:
break # stop the whole loop when i is 3
print(i)
# prints 0, 1, 2 (never reaches 3 or 4)
Trace: i is 0 (not 3, print 0), 1 (print 1), 2 (print 2), then 3 — the if fires, break runs, and the loop ends on the spot. The values 3 and 4 are never printed. Now compare continue, which only skips one iteration rather than ending the loop:
for i in range(5):
if i == 2:
continue # skip the rest of THIS iteration when i is 2
print(i)
# prints 0, 1, 3, 4 — 2 is skipped, but the loop keeps going
Trace: 0 prints, 1 prints, then i is 2 — continue jumps past print(i) to the next iteration, so 2 is missing. Then 3 prints and 4 prints. The loop did not stop; it just declined to do its work for that one value.
The accumulator pattern
Here is the single most useful loop pattern you will ever learn. An accumulator is a variable that lives outside the loop and builds up a result a little at a time across the iterations. You set it to a starting value before the loop, then each iteration updates it, and after the loop ends it holds the finished answer.
The classic example is adding up the numbers 1 through 10. The accumulator is a running total. It starts at 0, and each iteration adds the current number onto it:
total = 0 # the accumulator, starting empty (at 0)
for n in range(1, 11): # n takes 1, 2, 3, ..., 10
total = total + n # add this number onto the running total
print(total)
# prints 55 (1 + 2 + 3 + ... + 10)
The line total = total + n reads "make the new total equal to the old total plus n." Watch the running total grow, step by step:
- start: total = 0
- n = 1 → total = 0 + 1 = 1
- n = 2 → total = 1 + 2 = 3
- n = 3 → total = 3 + 3 = 6
- n = 4 → total = 6 + 4 = 10
- n = 5 → total = 10 + 5 = 15
- n = 6 → total = 15 + 6 = 21
- n = 7 → total = 21 + 7 = 28
- n = 8 → total = 28 + 8 = 36
- n = 9 → total = 36 + 9 = 45
- n = 10 → total = 45 + 10 = 55
- loop ends → total = 55
The accumulator does not have to be a sum. A very common variation is counting how many items meet some test. Here the accumulator starts at 0 and we add 1 only when the current number is even (an even number divides by 2 with no remainder, which n % 2 == 0 checks):
evens = 0 # accumulator: how many evens have we seen?
for n in range(1, 11):
if n % 2 == 0: # is n even?
evens = evens + 1 # yes -> bump the count by 1
print(evens)
# prints 5 (the evens are 2, 4, 6, 8, 10)
Same shape every time: set up an accumulator before the loop, update it inside the loop, read it after. Summing, counting, finding the biggest, joining text together — they are all this one pattern with a different update line.
Nested loops
You can put a loop inside another loop. This is called a nested loop. The outer loop runs once for each of its values, and for each of those, the entire inner loop runs from start to finish.
for a in range(3): # outer: a = 0, 1, 2
for b in range(2): # inner: b = 0, 1 (runs fully for each a)
print(a, b)
# prints: 0 0 / 0 1 / 1 0 / 1 1 / 2 0 / 2 1 (six lines)
Count the output: the outer loop runs 3 times, and each time the inner loop runs 2 times, so the body runs 3 × 2 = 6 times total. That multiplication is the whole point — and the whole danger. If both loops walk over a collection of size n, the body runs n × n times, which we write n². For n = 10 that is 100 steps; for n = 1000 it is a million. The work grows much faster than the data.
That "work grows like n²" idea is the seed of Big-O notation, the language programmers use to talk about how fast (or slow) code gets as the data grows. A single loop over n items is O(n); a loop inside a loop is usually O(n²). You will study Big-O properly in a later lesson — for now just remember that nesting loops multiplies the work.
Why it matters
You now have the two loops (for and while), the two steering keywords (break and continue), and the accumulator pattern that powers most real programs. The next big step is wrapping a useful chunk of code — loop and all — into a named, reusable tool you can call by name. That tool is a function, and it is the subject of the next lesson.
Where this leads: loops scan collections; later you meet the sliding-window pattern — a loop that never restarts from zero. See Where this leads next.
Practice
Write your first real loop and run it live.
Write sum_to_n(n) that returns the sum of every whole number from 1 up to and including n. For example sum_to_n(5) is 1 + 2 + 3 + 4 + 5 = 15. If n is 0, the sum is 0.
Checkpoint
Repeating work: loops
Pass to unlock the Next button belowNext: Functions →