Loops (for and while)
Repeat work safely with for and while loops, range steps, break and continue, loop else, enumerate, zip with strict length checks, and the iterator protocol
A café owner updates loyalty points for 4,800 customers. She writes the same two lines at the top of a spreadsheet, copies them down, and stops when the scrollbar looks about right. One row is missed. A week later a regular customer with twelve visits shows zero points, and the support ticket takes an hour to trace.
The same failure appears in every office. People repeat a step by hand, the copy stops one row short, and nobody notices until a total disagrees. A program names the list once and repeats the work for every item, then adds the control switches that stop early or skip a record.
Key terms
- A statement that runs a block of code more than once, either once per item or while a condition stays true.
- Any object a for loop can walk through, such as a list, a string, a dictionary, a file or a range.
- The object that hands out one item at a time when a loop asks for the next value.
- The indented block under the loop header that runs once for each pass.
- A lazy sequence of whole numbers defined by a start, a stop and an optional step.
- A name that collects a running result, such as a total or a count, across loop passes.
- A special value that marks the end of input, such as a price of zero or the word quit.
- A loop placed inside another loop, where the inner loop finishes for every outer item.
Loop
Iterable
Iterator
Loop body
range
Accumulator
Sentinel
Nested loop
This lesson is the sixth stop of the course. By the end you will write both loop forms, predict the numbers a range produces, and control a loop with break and continue. You will also read the iterator machinery under the for keyword, and every claim here was checked against the Python 3.15 documentation.
1. The for Statement: One Instruction, Every Item
Start with the café list. A for statement names a collection once and runs an indented block once per item. The name after for is bound to the current item, and that name is available inside the block.
orders = ["tea", "coffee", "juice"]
for order in orders:
print(f"Make one {order}")The output is one line per drink:
Make one tea
Make one coffee
Make one juiceNothing there counts items, checks a length or advances an index; Python binds
order to each item in turn and stops when none remain.
The same shape works on every iterable, not only lists. Strings hand out one character at a time, and dictionaries hand out their keys in insertion order, which Python has guaranteed since version 3.7.
for letter in "tea":
print(letter)The loop prints t, e and a on separate lines. A dictionary loop visits
its keys in that same insertion order, and the next lesson shows how to read
the values instead.
A first accumulator
An accumulator is a name that carries a result from one pass to the next. The pattern needs three parts: a starting value before the loop, an update inside the loop, and a read after the loop. Money stays in integer cents here, which keeps the arithmetic exact.
prices_cents = [250, 400, 125]
total_cents = 0
for price in prices_cents:
total_cents += price
print(f"Total: {total_cents / 100:.2f}")The loop adds 250, then 400, then 125, so total_cents ends at 775 and the
program prints Total: 7.75. The accumulator starts before the loop because a
value defined inside the body would be rebuilt on every pass.
A trace table with one row per pass is the cheapest way to check a loop by hand; write down each name after the body runs.
Two details about the loop name surprise beginners. The name keeps its last
value after the loop ends, so order still holds juice on the next line. If
the loop never runs, the name was never bound, and reading it raises
NameError.
📚 Did you know? The loop variable survives after the loop on purpose.
Python has no block scope for a for statement, so order belongs to the
surrounding function or module. That is handy after a search, and it is also
why an empty loop can leave a name undefined.
2. range: Counting Without Building a List
Most repetition needs a number, not a stored collection. The range built-in
produces a lazy sequence of whole numbers, and it never builds a list of them.
It stores a start, a stop and a step, then computes each value on demand.
The stop value is always exclusive. A call to range(5) yields
0, 1, 2, 3 and 4, and it never yields 5. This single rule explains most
off-by-one mistakes in beginner code.
Call | Values produced | Count |
|---|---|---|
range(5) | 0, 1, 2, 3, 4 | 5 |
range(2, 6) | 2, 3, 4, 5 | 4 |
range(2, 9, 2) | 2, 4, 6, 8 | 4 |
range(10, 0, -1) | 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 | 10 |
range(5, 0, -2) | 5, 3, 1 | 3 |
range(0) | nothing | 0 |
A negative step counts down, and the stop stays exclusive. The call
range(5, 0, -2) stops before zero, so the last value is 1. The call
range(0) is empty and falsy, which makes it a clean way to skip work when a
count is zero.
A range is a real sequence, so len(), indexing and membership work.
The membership test follows the step, which is why 4 is not in
range(2, 10, 3). The values 2, 5 and 8 are in that range, and 8 is the last
one below 10.
>>> 4 in range(2, 10, 3)
False
>>> 8 in range(2, 10, 3)
TrueThe sum of 1 through 100 is 5050, and the formula 100 * 101 / 2 agrees with
the loop.
total = 0
for n in range(1, 101):
total += n
print(total) # 5050The countdown exercise uses a negative step and prints from 10 down to 1. The final message sits after the loop, so it runs exactly once.
for n in range(10, 0, -1):
print(n)
print("Blast off!")range(1, 10) produces 1 through 9, not 1 through 10. When a human says
"one to ten", the loop needs range(1, 11). Say the stop value out loud as
"up to but not including" until the habit sticks.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A Python program runs `print(list(range(2, 10, 3)))`. Which list is printed?
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A countdown wants the values 5, 3 and 1 in that order. Which call produces exactly that list when wrapped in `list()`?
3. while: Repeat Until the World Changes
A for loop walks a collection you can name. A while loop repeats while a condition stays truthy, and it re-tests that condition before every pass. Use it when the number of passes depends on something the program discovers as it runs.
count = 5
while count > 0:
print(count)
count -= 1
print("Liftoff")The condition is checked at 5, 4, 3, 2 and 1. After the body lowers count to
0, the next test fails and the loop ends. The printed countdown is 5, 4, 3, 2,
1 and then the final message.
Every correct while loop initialises the names its condition reads, tests the condition in the header, and changes one of those names inside the body. Remove the update and the condition never changes, so the loop runs forever.
Input validation is the natural home for this shape. The loop keeps asking until the answer passes a test, and the program never needs to know in advance how many attempts a person will make.
password = ""
while not password:
password = input("Enter a non-empty password: ")
print("Password accepted")An empty string is falsy, so not password is true at the start. Once the user
types any text, the condition becomes false and the loop ends. A sentinel works
the same way with a value chosen by the data rather than by emptiness.
total_cents = 0
price_cents = int(input("Price in cents, 0 to stop: "))
while price_cents != 0:
total_cents += price_cents
price_cents = int(input("Price in cents, 0 to stop: "))
print(f"Total: {total_cents / 100:.2f}")The sentinel is 0, and the loop stops before adding it. A shopper can enter any number of prices, and the program still ends on the agreed marker.
The state diagram below shows the same cycle with the exit paths labelled. The normal exit reaches the else block, while a break jumps straight past it.
Description
A state diagram for the section 3. while: Repeat Until the World Changes, showing the states and transitions described in the surrounding text.
A while loop with no update is an infinite loop, and so is a loop whose
update sits after a continue. Check that every path through the body can
reach the statement that changes the condition. The = and == mix-up is a
different failure: while count = 5: is a syntax error, not a long loop.
Some infinite loops are intentional. An event loop may use while True: and
leave through a break; the difference from a bug is a reachable exit path.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A program sets `count = 0` and then runs `while count < 5:` with a body that prints `count`, runs `continue`, and only then runs `count += 1`. What happens?
4. break, continue and pass
Three small statements control the flow inside a loop, and each solves a different problem.
break leaves the innermost loop immediately. The rest of the body is skipped,
the loop condition is not tested again, and execution continues after the loop.
for n in range(100):
if n == 5:
break
print(n)Only 0, 1, 2, 3 and 4 are printed. The range still has 95 more values, and none of them is visited.
continue skips the rest of the current pass and moves to the next item or the
next condition test. The loop itself keeps going.
for n in range(10):
if n % 2 == 0:
continue
print(n)The even numbers are skipped, so the output is 1, 3, 5, 7 and 9. The loop visits all ten numbers; it simply does not print half of them.
pass does nothing at all. It exists because Python needs a statement in a
block, and a bare header with no body is a syntax error. Use it while a body is
still being designed, never as a way to hide work you forgot to write.
for item in queue:
pass # placeholder until the body is designedThe flowchart below follows one for loop from the first item to the exit. The break edge leaves the cycle, the continue edge returns for another item, and the normal path reaches the else block.
Description
A flowchart for the section 4. break, continue and pass, showing the path and decision points described in the surrounding text.
When a loop has several exit conditions, consider while True: with a clear
break for each one. A single readable exit often beats a header condition
that grows into a long chain of and and or terms.
How to answer
Multiple-choice question with 2 options. Select one option, then choose Check Answer.
A program has an outer for loop and an inner for loop. The inner body runs `break` on its first pass. Does that break also leave the outer loop?
5. The Loop else: Search and Not Found
A loop can carry an else block, and it is not a partner to an if statement. The else block runs only when the loop finishes without hitting a break. It also runs when the iterable was empty, because no break occurred.
That rule turns the else block into a clean search idiom: break on a hit, and let else report the miss.
names = ["Ada", "Grace", "Linus"]
target = "Grace"
for name in names:
if name == target:
print(f"Found {target}")
break
else:
print(f"{target} not found")The loop reaches Grace, prints Found Grace, and breaks. Because a break
happened, the else block is skipped. Change the target to Alan and the loop
finishes normally, so the else block prints Alan not found.
The prime search is the classic nested version. The inner loop tests possible divisors, and its else block runs only when no divisor caused a break.
for n in range(2, 20):
for divisor in range(2, int(n ** 0.5) + 1):
if n % divisor == 0:
break
else:
print(n)The output is 2, 3, 5, 7, 11, 13, 17 and 19. The else belongs to the inner for loop, so it runs once per candidate number that survived every divisor test.
The loop else is about the loop's own exit, not about an if condition. Read
for ... else as "for ... and if no break happened, then ...". Teams that
avoid the construct often set a found flag instead, and both forms are
correct when the intent is clear.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A list contains three names and the loop searches for one of them. The name is found on the second item, and the body runs `break` right after printing it. What does the loop's else block do?
6. Nested Loops and the Multiplication of Work
A nested loop is a loop inside another loop. The inner loop runs from start to finish for every single item of the outer loop, so the total number of body runs is the product of the two counts.
for row in range(1, 4):
for column in range(1, 4):
print(f"{row * column:3}", end="")
print()The outer loop runs three times and the inner loop runs three times per outer pass, so the body runs nine times. The output is a small multiplication table:
1 2 3
2 4 6
3 6 9Nested loops also multiply cost: 1,000 orders with 1,000 products each runs one million times, even though each loop looks small on its own.
A break inside a nested loop leaves only the inner loop. If the outer loop must stop too, set a flag and test it in the outer body, or move the work into a function and return from it. Assuming one break ends the whole nest is a common source of duplicated output.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
An outer loop runs `for row in range(3):` and an inner loop runs `for column in range(4):` for every outer pass. How many times does the inner body execute in total?
7. enumerate and zip: Index and Pairing
Two built-ins remove most manual counters and most index arithmetic from loops.
enumerate adds a position to each item, and zip walks several iterables in
parallel.
enumerate(iterable, start=0) yields a pair on every pass: the position and
the item. The start defaults to 0 and may be any integer, which is useful when
a human-facing list should begin at 1.
colors = ["red", "green", "blue"]
for position, color in enumerate(colors, start=1):
print(f"{position}: {color}")The output is 1: red, 2: green and 3: blue.
zip(*iterables) yields tuples that pair items position by position.
It stops at the shortest input, which is convenient when the shorter list is
the real limit and dangerous when a missing item is a bug.
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92]
for name, score in zip(names, scores):
print(f"{name}: {score}")
for name, score in zip(names, scores, strict=True):
print(f"{name}: {score}")The first loop prints only Alice: 85 and Bob: 92, so Charlie disappears
without a warning. Python 3.10 added strict=True through PEP 618, and the
second loop turns that silent drop into
ValueError: zip() argument 2 is shorter than argument 1.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A program runs `for place, fruit in enumerate(["apple", "pear"], start=1):` and prints both names on each pass. Which pairs are produced?
How to answer
Multiple-choice question with 2 options. Select one option, then choose Check Answer.
A program pairs three names with two scores using plain `zip(names, scores)`, and then pairs the same lists with `zip(names, scores, strict=True)`. Does the strict version raise an error while the plain version silently drops the third name?
How to answer
Matching exercise with 6 pairs. Match each item on the left with its answer on the right.
Match each loop construct to the behaviour it produces.
Drag answers from here:
How to answer
Fill-in-the-blank exercise with 3 blanks. Type the missing words, then check your answer.
Complete the two loop calls. The first range should print 1 through 10, and the zip call should reject lists of different lengths.
for n in range(blank, blank): print(n) and for name, score in zip(names, scores, blank=True): print(name, score)
Word Bank, drag words into the blanks above:
8. Under the Hood: the Iterator Protocol
The for keyword looks like a single statement, but it runs a small protocol
behind the scenes. iter(obj) returns an iterator, and next(it) returns the
next item or raises StopIteration when the items run out.
You can drive that protocol by hand. The loop below produces exactly the same three colours as a for statement, and the try block turns the final exception into a normal exit.
colors = ["red", "green", "blue"]
iterator = iter(colors)
while True:
try:
color = next(iterator)
except StopIteration:
break
print(color)The sequence diagram shows the for statement, the iterator and the list. Every item is one request and one reply, and the last reply ends the loop.
Description
A sequence diagram for the section 8. Under the Hood: the Iterator Protocol, showing the order of messages and replies described in the surrounding text.
You rarely write that manual loop, and that is the point. The for statement is
the readable wrapper around the protocol, and it works on any object that
supports iter and next, including files and generators.
How to answer
Ordering exercise with 6 items. Move them into the correct order, then check your answer.
Order the steps that a for loop performs for one item, from first to last.
9. The Mutate-While-Iterating Trap
A loop over a list is not a snapshot of that list. The iterator walks positions in the live object, so adding or removing items during the pass changes what the next position contains.
items = [1, 2, 3, 4]
for item in items:
if item % 2 == 0:
items.remove(item)
print(items)The result is [1, 3], but the path is not the tidy one most people expect. The
loop removes 2, which shifts 3 into the position the iterator has already
passed. The next pass therefore sees 4, and 3 is never examined at all.
The skipped value is the real damage: a filter that looks correct can leave a bad record behind, and the bug depends on the order of the data.
Two safe patterns cover almost every case. Iterate over a copy when you must
remove from the original, or build a new list with append and leave the
original alone.
items = [1, 2, 3, 4]
for item in items[:]:
if item % 2 == 0:
items.remove(item)
print(items) # [1, 3]The slice items[:] makes a shallow copy, so the loop walks the old sequence
while removals change the new one. Building a new list avoids mutation, and it
is easier to test because the input never changes.
Never add or remove items from a list while a loop is walking that same list, unless you have tested the exact order and the exact result. The failure is silent, it depends on position, and it often appears only with real data.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A list holds `[1, 2, 3, 4]`, and a loop walks it directly while removing every even value with `items.remove(item)`. Which value is never examined by that loop?
10. Choosing for or while
The choice between the two forms is about what controls the repetition. If you can name the collection or the count, use for. If the exit depends on a value the body discovers, use while.
A for loop is usually clearer when both forms would work, because the header states the source of the items and no manual update can be forgotten. A while loop earns its place when its condition is the real subject of the code.
The quadrant below places common patterns by how well the count is known and whether the exit is collection driven or condition driven.
Description
A quadrant chart for the section 10. Choosing for or while, placing the options along the two axes described in the surrounding text.
11. Loops in the Python 3.15 World
2026 Updates (as of November 2026)
What changed recently, and why older tutorials look different. Every line below was checked against a primary source in November 2026:
- Python 3.15.2 is the current stable release (3.15.0 shipped on 2026-10-09, and 3.15.2 followed on 2026-10-25). The loop syntax in this lesson is unchanged in 3.15, so the examples run as written.
- Python 3.10 reached end of life on 2026-10-01. The
zip(strict=True)option arrived in 3.10 through PEP 618, and it is available on every supported series today.
📚 Did you know? A range object does not hold a list of its numbers. It
stores the start, stop and step and produces values on demand, so
range(1_000_000_000) uses the same small amount of memory as range(5).
Real-World Case Studies
Meta. Meta's engineering blog on 30 June 2026 called Python its most used language and pointed to the Instagram and Threads backends plus PyTorch. At that scale, work repeats over feeds, users and model batches, and a for loop over a collection is the basic shape of that repetition.
Netflix. The Netflix Tech Blog in April 2019 described Python across the full content lifecycle, including the tooling behind the Open Connect CDN. A catalog walk that visits every title is a loop over a sequence, like encoding checks and release reports.
Dropbox. Dropbox published on dropbox.tech in 2018 and 2019 about running its desktop client on Python and completing one of the largest Python 2 to Python 3 migrations. That migration later used type checking across close to four million lines, and work of that size needs loops that visit file after file.
📚 Did you know? A loop can finish without running its body at all, and
the else block still runs. for item in []: ... else: print("empty") prints
empty, because the loop completed normally and no break occurred.
Practice Questions
Answer these before the next lesson; the final two questions reach back to earlier lessons.
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A program runs `x = 3`, then `y = x + 1`, then `x = "three"`. What does `y` hold after the third line, and why?
How to answer
Multiple-choice question with 4 options. Select one option, then choose Check Answer.
A Python expression reads `2 + 3 * 4 ** 2`. Which value does it produce under the standard operator precedence rules?
What Comes Next
You can now repeat work over a collection, count with range, and leave a loop on your own terms. The next lesson turns single values into ordered collections, and every list or tuple you meet becomes a natural home for the for loops you just learned.
Before moving on, the video below is a full beginner course that replays this material in a second voice. Its loop chapters are a good parallel reference when you want the same ideas from another teacher.
Description
A video walkthrough that supports the section What Comes Next and repeats its main ideas in a second voice.
Dig deeper
- The official tutorial on for, range, break, continue and loop elsePython 3.15 documentation
- Range objects, membership and negative stepsPython 3.15 documentation
- Built-in functions for enumerate, zip, iter and nextPython 3.15 documentation
- PEP 618, optional length checking for zipPEP index
- PEP 234, iterators and the protocol behind forPEP index
- Python for loops, a practical walkthroughReal Python
Key Takeaways:
- A for loop binds a name to each item of an iterable and runs its body once per item. It needs no counter, no length check and no manual index.
range(stop)stops before the stop value,range(start, stop, step)advances by the step, and a negative step counts down.- A while loop re-tests its condition before every pass, so the body must change something the condition reads or the loop never ends.
breakleaves the innermost loop,continueskips the rest of the current pass, andpassfills a body without doing anything.- A loop else block runs only when the loop finished without a break, and it also runs for an empty iterable.
- Nested loops multiply their iteration counts, and a break inside the inner loop does not leave the outer one.
enumerateadds a position to each item, plainzipstops at the shortest input, andzip(strict=True)from PEP 618 raises ValueError on a length mismatch.- A for loop is the readable wrapper around
iter()andnext(), ending whenStopIterationis raised. - Mutating a list while iterating over it skips or repeats items, so walk a copy or build a new list instead.
References
Every factual claim links to its source, with the access date printed so the source can be rechecked.