Iterating Through Tuples, Lists, and Dictionaries with a While Loop
🔁 Iterating Through Tuples, Lists, and Dictionaries with a While Loop
A for loop walks a collection for you — grab the next item, run the body, repeat until there isn't one left. A while loop does none of that bookkeeping automatically: you track where you are, you decide when to stop, and you remember to move forward each time. That's more manual work, but it's also more flexible — a while loop can keep going until a condition becomes false for reasons that have nothing to do with "reached the end of the list," which is exactly what makes it the right tool for interactive, sentinel-controlled input loops like this lesson's second example.
for (init; condition; increment) loop bundles all three parts on one line, which is exactly why it's so hard to forget the increment — it's sitting right there next to the condition. Python's while loop has no such structure: the initialisation happens before the loop, the condition is checked at the top, and the increment is just another statement somewhere in the body — easy to place, and just as easy to accidentally leave out. Every "infinite loop" bug in this lesson traces back to that one structural difference.
🧱 The Anatomy of a Manual Loop
Every while loop that walks a collection needs the same three ingredients — and it's the programmer's job to supply all three, not the language's.
stop yet").do...while in Java/JS, which Python doesn't have.break vs continuebreak exits the closest enclosing loop immediately — no more condition checks. continue jumps straight back to the condition check, skipping whatever's left in the body for this iteration only.📖 Building a Dictionary with a Counting While Loop
The simplest shape: an index that starts somewhere other than zero, an upper bound, and a dictionary that grows by one key per iteration.
i does double duty here — it's both the counter that controls the loop and the source of each key and value.d = {}
i = 20
while i < 41:
key = f"number{i}"
value = i
d[key] = value
i += 1
print(d)
# → {'number20': 20, 'number21': 21, ..., 'number40': 40}
< 41, Not < 40while gives you no scaffolding to lean on, so it's worth spelling out explicitly.# The task wants keys number20 through number40 inclusive — 21 keys total.
# i starts at 20. The loop must still run when i == 40 (the last one wanted),
# so the condition has to still be True at i == 40, and False at i == 41.
# "i < 41" is exactly that boundary. "i < 40" would stop one key short —
# a classic off-by-one, easy to miss because the loop still runs fine,
# it just quietly produces the wrong result rather than crashing.
⌨️ Input-Driven Dictionary Building — Sentinels, break, and Nested Validation
This is the shape a for loop genuinely can't replace: there's no fixed collection to walk, no known length in advance — just "keep going until the user tells you to stop." That's a while True loop's natural home.
while True keeps the whole thing running until "stop"; each inner while len(...) == 0 is a small validation loop that re-prompts until the user types something.d = {}
while True:
key = input("Please enter a key (stop to quit): ")
if key == "stop":
break
while len(key) == 0:
key = input("Type something please!")
continue
value = input("Please enter a value (stop to quit): ")
if value == "stop":
break
while len(value) == 0:
value = input("Type something please!")
continue
d[key] = value
if len(d) == 0: print("The Dictionary is empty.")
else: print(d)
continue Actually Doing Here?continue looks unnecessary — the loop would re-check the condition and loop back anyway once it hits the bottom of the block. It's not wrong, just redundant.while len(key) == 0:
key = input("Type something please!")
continue # ← this is the LAST line in the loop body anyway,
# so control would land back at "while len(key) == 0"
# the moment this line finished, with or without continue.
# continue only changes behaviour when there's code AFTER it in the body
# that you want to skip. Here there isn't any — so it's harmless, but
# worth recognising as a no-op rather than assuming it's doing something
# subtle that "while len(key) == 0:" alone wouldn't already do.
🔬 Case Study: Deduplicating a Tuple While Preserving Order
This one was a genuinely tricky tutor exercise — a good excuse to slow down and build the solution up piece by piece, including the wrong turns a while-loop version of this problem tends to invite.
for loop (or even a one-liner using dict.fromkeys(), since dicts preserve insertion order in modern Python). It was solved with while loops here specifically because that was today's constraint, not because while is the right tool for this job. Recognising when a problem wants a for loop instead is itself a useful skill — you'll see the for-loop version at the end of this section for comparison.
words_tuple = ("who", "why", "when", "who", "when",
"what", "where", "whom", "who", "when", "who")
# Wanted: ("who", "why", "when", "what", "where", "whom")
# — first occurrence of each word, original order, no repeats
Building It Up, Step by Step
new_tuple = (), and an index to walk words_tuple one position at a time — the outer loop's job is "look at every word, once."words_tuple[index], the question is: "have I already put this word into new_tuple?" Answering that means searching new_tuple — which, with only while loops available, means a second, inner loop with its own counter, new_index.found flag starts False before the inner search begins. If the inner loop ever matches words_tuple[index] == new_tuple[new_index], set found = True and break — there's no need to keep scanning once a match turns up.break-ed early or ran all the way through), check found. If it's still False, the word is new — append it with new_tuple = new_tuple + (words_tuple[index],) (a real .append() doesn't exist for tuples, since they're immutable — concatenation builds a brand-new tuple instead).index, unconditionally, every time round — regardless of whether this word turned out to be a duplicate or not. This step is easy to accidentally place somewhere it only sometimes runs, which is exactly how infinite loops happen here (see the pitfalls below).while loops, one nested inside the other, each with its own counter, condition, and advance step — exactly the anatomy from the top of this lesson, applied twice.words_tuple = ("who", "why", "when", "who", "when",
"what", "where", "whom", "who", "when", "who")
new_tuple = ()
index = 0
while index < len(words_tuple):
new_index = 0
found = False
while new_index < len(new_tuple):
if words_tuple[index] == new_tuple[new_index]:
found = True
break
new_index += 1
if not found:
new_tuple = new_tuple + (words_tuple[index],)
index += 1
print(new_tuple)
# → ('who', 'why', 'when', 'what', 'where', 'whom')
Pitfalls This Exercise Loves to Set
new_index += 1 gets indented one level too far — inside the if block — it only runs on a match. On every non-match, new_index never changes, the inner while condition stays true forever, and the program hangs. The increment belongs at the same indentation as the if, not inside it.break only exits the inner loopbreak here escapes the while new_index < len(new_tuple): search — it does not touch the outer while index < len(words_tuple): loop at all. The outer loop's own index += 1 still has to run afterwards, unconditionally, or the outer loop never advances either.index += 1 inside the outer if not foundif not found:. Once a genuine duplicate is found, found stays True, the increment is skipped, index never moves — and the outer loop spins on the same duplicate word forever..append() the result.append() — attempting new_tuple.append(...) raises AttributeError. Building a tuple incrementally always means rebinding the name via concatenation, new_tuple = new_tuple + (x,), creating a new tuple object each time.while version by hand — this is what "use the right tool for the job" looks like once the constraint of today's lesson is lifted.# Option A — a for loop, same explicit "have I seen this?" logic
new_tuple = ()
for word in words_tuple:
if word not in new_tuple:
new_tuple = new_tuple + (word,)
# Option B — dict.fromkeys() relies on dicts preserving insertion order
# and automatically dropping duplicate keys — both jobs done in one call
new_tuple = tuple(dict.fromkeys(words_tuple))
📋 Quick Reference — While-Loop Iteration Patterns
| Task | While-loop pattern | Notes |
|---|---|---|
| Walk a list/tuple by index | while i < len(seq): ...; i += 1 | The bread-and-butter pattern used throughout this lesson |
| Walk and mutate a list in place | while i < len(lst): lst[i] = ...; i += 1 | Safe — indexing a list doesn't change its length |
| Walk a dict's keys | keys = list(d); while i < len(keys): ... | Snapshot the keys first if you plan to modify d mid-loop |
| Search for a value (linear scan) | found = False; while ... and not found: ... | The inner loop in this lesson's case study |
| Loop until a sentinel value | while True: ...; if x == "stop": break | No fixed length — a for loop couldn't express this at all |
| Re-prompt until valid input | while len(x) == 0: x = input(...) | This lesson's Example 2 validation loops |
| "Append" to a tuple | t = t + (item,) | No .append() — tuples are immutable, this makes a new tuple |
| Exit a nested loop early | break | Only exits the closest enclosing loop — the outer loop still needs its own advance step |
There's no C-style for-loop scaffolding to lean on — Java/JavaScript's
for (i = 0; i < n; i++) puts the increment right next to the condition, so it's hard to forget. Python's while loop separates initialisation, condition, and advance into three unrelated-looking statements, and the advance step can end up buried anywhere in the body — including, as this lesson's pitfalls show, inside the wrong if branch.break only exits one loop, same as Java/JS — Python has no labelled break the way Java does (break outerLabel;). If you genuinely need to exit two nested loops at once, the idiomatic Python approach is a flag variable checked in the outer loop's condition, or wrapping the loops in a function and using return.continue jumps to the condition check, not "the next iteration of a counted range" — in a while loop, if your advance step (i += 1) sits after a continue, that advance gets skipped entirely, and you're right back to an infinite loop. This is a real trap in while loops that doesn't exist the same way in a Java/JS C-style for, where the increment clause runs regardless of continue.Python has no
do...while — if you need a loop body to run at least once before its condition is ever checked, the common workaround is while True: with a break at the point the condition should apply (exactly the pattern in this lesson's Example 2).