Visual walkthrough — Python syntax, data types, control flow
Before we can talk about a for loop, we need three plain-English ideas, each earned with a picture:
- an iterable — a thing that can be walked through,
- an iterator — the little "bookmark" that remembers where you are,
StopIteration— the polite way the bookmark says "I'm done."
Let's build them.
Step 1 — What is a "collection" of values? (the raw material)
WHAT. Start with the simplest object: a list. Written [10, 20, 30], it is just three boxes sitting in a row, each holding one value.
WHY. A for loop's whole job is to hand you one value at a time out of some group of values. Before we can talk about handing them out, we must picture the group itself. A list is the most concrete group — fixed boxes in a fixed order.
WHAT IT LOOKS LIKE. Three labelled boxes. Each box has a position number (Python calls it an index) starting at 0, not 1. The value inside box 0 is 10.
Here every symbol is earned:
[10, 20, 30]— the list itself, the whole row of boxes.- index
0, 1, 2— the position numbers; boxiis reached bymylist[i]. len([10,20,30])— how many boxes there are.
That's the raw material. But a list by itself has no notion of "where am I now." It's a static row. The looping is done by something else.
Step 2 — The bookmark: what an iterator is
WHAT. We introduce a second object called an iterator. Think of it as a tiny arrow — a bookmark — that points at exactly one box and remembers that position.
WHY. The list doesn't move. Something has to track progress. If the list itself remembered "I'm at box 1," you could only ever loop it once at a time. Python separates the data (the list) from the cursor (the iterator) so you can even have two loops walking the same list independently.
WHAT IT LOOKS LIKE. The red arrow below is the iterator. Right now it sits just before box 0, cocked and ready to fire.
You create this bookmark with the built-in function iter:
box = [10, 20, 30]
cursor = iter(box) # cursor is now the red arrow, aimed at box 0iter(box)— the WHAT: "please make me a fresh bookmark for this iterable."cursor— the bookmark object; it holds a hidden position counter, starting before box0.
Key mental split: box = the shelves. cursor = the finger walking along them.
Step 3 — The single magic move: next()
WHAT. Ask the bookmark for its next value with the built-in next(cursor). This does two things in one motion: it reads the box the arrow points at, then slides the arrow forward one box.
WHY. A loop is nothing more than this one move, repeated. If we understand next completely, we understand the whole for loop — the for statement is just sugar that calls next over and over for you.
WHAT IT LOOKS LIKE. Watch the red arrow jump. Call next once → you get 10, arrow moves to box 1. Call it again → you get 20, arrow moves to box 2.
next(cursor) # returns 10, arrow now on box 1
next(cursor) # returns 20, arrow now on box 2
next(cursor) # returns 30, arrow now PAST the endStep 4 — The edge of the shelf: StopIteration
WHAT. After we've read box 2 (value 30), the arrow is pointing past the last box — at empty space. Call next one more time and there is nothing to read. Python does not return None and does not crash silently. It raises a special signal called StopIteration.
WHY. The loop needs a crisp, unmistakable "we are done" message. A returned value like None would be ambiguous — what if None was a legitimate element? So Python uses an exception (a raised signal) purely as an end-of-data flag. This is the degenerate case that every finite loop must eventually hit.
WHAT IT LOOKS LIKE. The arrow has fallen off the right end of the shelf. The next request comes back with a red "STOP" — not a value.
next(cursor) # raises StopIteration ← the shelf is empty hereStep 5 — Assembling the for loop from these parts
WHAT. Now we show that for x in box: is exactly Steps 2–4 wired together automatically. Python builds the cursor, calls next in a loop, hands each value to your variable x, and quietly stops when StopIteration appears.
WHY. This is the payoff. Everything you write as a for loop unfolds into this hidden machine. Seeing the unfolding removes all mystery about loop variables, "why does it stop," and "why can't I reuse an exhausted iterator."
WHAT IT LOOKS LIKE. Two columns side by side: the friendly for on the left, its hidden mechanical form on the right, joined by arrows showing they are the same thing.
# What you write:
for x in box:
print(x)
# What Python actually does (the hidden machine):
cursor = iter(box) # Step 2: make the bookmark
while True:
try:
x = next(cursor) # Step 3: read + advance
except StopIteration: # Step 4: the shelf is empty
break # leave the loop, no error shown
print(x) # your loop body runs with x = 10, 20, 30Each for pass = one next. The loop body is whatever you indented under for (recall from the parent: indentation *is* the block).
Step 6 — The degenerate case: an empty iterable
WHAT. What if box = [] — zero boxes? The for body should run zero times, cleanly.
WHY. Every correct loop model must handle the empty case, or code that processes "maybe-empty" data (an empty dataset, a filtered-out batch) would crash. We check that our machine handles it.
WHAT IT LOOKS LIKE. The arrow is created already sitting past the (non-existent) end. The very first next raises StopIteration, so the body never runs even once.
box = []
cursor = iter(box)
next(cursor) # StopIteration on the FIRST call → body runs 0 timesTrace it through Step 5's machine: iter([]) builds a cursor already at the end; the first next throws StopIteration; break fires immediately; print never runs. No special case needed — the same machine just does nothing. That is the beauty of the design.
Step 7 — range produces values without any shelves
WHAT. range(3) gives you 0, 1, 2 — but there is no list of boxes stored anywhere. range is a lazy iterable: it computes each value the instant next asks for it.
WHY. This is why for i in range(1000000) doesn't eat a million boxes of memory. It also explains the parent's line range(0, 100, 32) → [0, 32, 64, 96]: the values are generated on demand by simple arithmetic, not looked up.
WHAT IT LOOKS LIKE. Instead of a shelf, a little formula-machine. The arrow is now a counter; each next computes and then checks whether it has reached stop.
The one-picture summary
Everything above, compressed: a list feeds iter → which makes a cursor → the for loop spins the cursor with repeated next → each value flows into the body → until StopIteration closes the door.
Recall Feynman retelling — say it back in plain words
Imagine a shelf of numbered boxes (that's the list, the iterable). To walk it, you don't move the shelf — you make a finger that points at box 0 (that's iter, giving you an iterator). Every time you say next, the finger reads its box and slides one box right. A for loop is just you saying next over and over, dropping each value into a variable and running the indented code. When the finger slides off the far end, the shelf shouts StopIteration — not an error, just "done" — and the loop quietly stops. If the shelf was empty to begin with, the very first next shouts immediately, so the loop body never runs. And range is a magic shelf with no boxes at all: it computes each number on the spot with and stops the instant it would reach stop. That's the whole story — one finger, one next, one stop signal.
Recall
What does iter(x) return, in one word? ::: A bookmark — an iterator that tracks position.
What two things does a single next() call do? ::: Reads the current value, then advances the cursor by one.
Is StopIteration an error you must fix? ::: No — it is the normal end-of-data signal the for loop catches for you.
How many times does the body of for x in []: run? ::: Zero — the first next raises StopIteration immediately.
Does range(0, 100, 32) include 100? ::: No — stop is never produced; it yields 0, 32, 64, 96.
Where this leads: the "lazy" idea in Step 7 is the seed of generators; the StopIteration-as-signal idea connects to exception handling in data pipelines; and looping over rows powers both NumPy and Pandas (though there you'll learn to avoid Python loops for speed).