Loops (for and while)
Beginner⏱55 minutes📖16 min readLesson 6 of 10

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

Loop
A statement that runs a block of code more than once, either once per item or while a condition stays true.
Iterable
Any object a for loop can walk through, such as a list, a string, a dictionary, a file or a range.
Iterator
The object that hands out one item at a time when a loop asks for the next value.
Loop body
The indented block under the loop header that runs once for each pass.
range
A lazy sequence of whole numbers defined by a start, a stop and an optional step.
Accumulator
A name that collects a running result, such as a total or a count, across loop passes.
Sentinel
A special value that marks the end of input, such as a price of zero or the word quit.
Nested loop
A loop placed inside another loop, where the inner loop finishes for every outer item.

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.

python
orders = ["tea", "coffee", "juice"] for order in orders: print(f"Make one {order}")

The output is one line per drink:

plaintext
Make one tea Make one coffee Make one juice

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

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

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

ℹ️Note

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

python
>>> 4 in range(2, 10, 3) False >>> 8 in range(2, 10, 3) True

The sum of 1 through 100 is 5050, and the formula 100 * 101 / 2 agrees with the loop.

python
total = 0 for n in range(1, 101): total += n print(total) # 5050

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

python
for n in range(10, 0, -1): print(n) print("Blast off!")
⚠️Warning

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.

Practice Question
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?

Practice Question
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.

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

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

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

100%
Description

A state diagram for the section 3. while: Repeat Until the World Changes, showing the states and transitions described in the surrounding text.

⚠️Warning

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.

Practice Question
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.

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

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

python
for item in queue: pass # placeholder until the body is designed

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

100%
Description

A flowchart for the section 4. break, continue and pass, showing the path and decision points described in the surrounding text.

💡Tip

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.

Practice Question
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.

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

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

📌Important

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.

Practice Question
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.

python
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:

plaintext
1 2 3 2 4 6 3 6 9

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

⚠️Warning

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.

Practice Question
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.

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

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

Practice Question
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?

Practice Question
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?

Matching Question
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.

for item in basket
Drop here
while queue
Drop here
break
Drop here
continue
Drop here
else on a loop
Drop here
zip with strict=True
Drop here

Drag answers from here:

visits every item in the collection
repeats while the condition stays truthy
leaves the innermost loop immediately
skips the rest of the current pass
runs only when no break happened
raises ValueError on unequal lengths
Fill in the Blanks
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:

1
11
strict
0
10
False

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.

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

100%
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.

Ordering Question
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.

Ask the collection for an iterator with iter()
Call next() to fetch an item
Bind the loop name to that item
Run the indented loop body
Call next() again for the following item
Stop when next() raises StopIteration

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.

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

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

⚠️Warning

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.

Practice Question
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.

100%
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)

ℹ️Note

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.
ℹ️Note

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

ℹ️Note

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

Practice Question
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?

Practice Question
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.

A long-form beginner course with dedicated sections on for loops, while loops and nested iterationWatch on YouTube
Description

A video walkthrough that supports the section What Comes Next and repeats its main ideas in a second voice.

✅Success

Key Takeaways:

  1. 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.
  2. range(stop) stops before the stop value, range(start, stop, step) advances by the step, and a negative step counts down.
  3. A while loop re-tests its condition before every pass, so the body must change something the condition reads or the loop never ends.
  4. break leaves the innermost loop, continue skips the rest of the current pass, and pass fills a body without doing anything.
  5. A loop else block runs only when the loop finished without a break, and it also runs for an empty iterable.
  6. Nested loops multiply their iteration counts, and a break inside the inner loop does not leave the outer one.
  7. enumerate adds a position to each item, plain zip stops at the shortest input, and zip(strict=True) from PEP 618 raises ValueError on a length mismatch.
  8. A for loop is the readable wrapper around iter() and next(), ending when StopIteration is raised.
  9. 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.

  1. [1]More Control Flow ToolsDocumentation

    Python Software Foundation · Accessed 2026-11-04

  2. [2]Built-in typesDocumentation

    Python Software Foundation · Accessed 2026-11-04

  3. Python Software Foundation · Accessed 2026-11-04

  4. [4]Compound statementsDocumentation

    Python Software Foundation · Accessed 2026-11-04

  5. [5]Built-in functionsDocumentation

    Python Software Foundation · Accessed 2026-11-04

  6. Python Software Foundation · Accessed 2026-11-04

  7. [7]PEP 234, iteratorsSpecification

    Python Software Foundation · Accessed 2026-11-04

  8. Python Software Foundation · Accessed 2026-11-04

Progress60%