The Quiet Terminal

← Beginner

for and while: making the computer repeat itself

2026-05-15 · 3 min read

Before loops, printing hello ten times means ten lines — or nine lines and one copy-paste typo. Loops are also where habits from other languages sneak in, so this post starts where I wish I'd started: in Python you loop over things, not over counters.

for: one thing at a time

names = ["Ada", "Grace", "Linus"]

for name in names:
    print("hello", name)

# hello Ada
# hello Grace
# hello Linus

The loop walks the list and hands you one item per turn; name is a fresh label for the current one, and the whole thing reads like the sentence it is: for each name in names, say hello.

range: counting, carefully

for n in range(1, 4):
    print(n)      # prints 1, 2, 3 — the stop value never prints

range(1, 4) means start at 1, stop before 4 — the stop is a fence you walk up to, never stand on. Everyone arriving from C or Java gets this wrong the same way: their loop instincts say "1 to 5" should be range(1, 5), and an iteration quietly vanishes. The honest translation of for (i = 0; i < n; i++) is range(n), full stop.

enumerate: when you need the number too

for i, name in enumerate(["Ada", "Grace", "Linus"], start=1):
    print(i, name)

# 1 Ada
# 2 Grace
# 3 Linus

My old habit was for i in range(len(names)) and then names[i] — it works, but it's a translation, not Python. These days, catching myself writing range(len(...)) is almost always the sign that I wanted enumerate, or that I didn't need the index at all.

break and continue: the two exits

log_lines = [
    "# build started",
    "compiled main.py",
    "ERROR: tests failed",
    "cleaning up",
]

for line in log_lines:
    if line.startswith("#"):
        continue          # skip this turn, go to the next
    if "ERROR" in line:
        break             # abandon the whole loop
    print(line)

# compiled main.py

continue abandons the current turn and jumps to the next one; break abandons the loop entirely. Here the comment line is skipped, the compile line gets printed, and the ERROR line ends the run before cleaning up ever gets a turn.

while: until something changes

queue = ["email Ada", "resize photos", "back up blog"]

while queue:                  # keep going while the list isn't empty
    task = queue.pop(0)       # take the first task off
    print("doing:", task)

attempts = 0
while attempts < 3:
    print("retrying")         # attempts never changes: this never stops
    # the fix is one line somewhere inside: attempts += 1

A while loop repeats as long as its condition is true, so something inside the loop has to move the guard. In the queue version, every pop() shrinks the list until it's empty, which Python reads as false, and the loop ends. Forget to move the guard and you've written the classic infinite loop — if you ever meet one live, Ctrl+C stops it in the terminal.

Which one when? for when you know what you're walking over — a list, a file, a range. while when you don't know how many turns it will take: until the user types quit, until the download finishes, until the queue drains. Nine of my ten loops are for; the while loop is for the scripts that sit in a terminal and wait for the world.