Python by Example

Loops

Python for and while loops, enumerate and zip for cleaner iteration, break and continue, and the for/else clause.

Python has two loop forms: for iterates over a collection or range, and while runs as long as a condition is true. Both support break (exit early) and continue (skip to the next iteration). Both also accept an else clause - uncommon but worth knowing.

for x in xs: works with any iterable - lists, ranges, strings, dicts, files. range(n) produces integers 0 through n-1. enumerate(xs) gives you each item paired with its index, removing the need for a separate counter. zip(a, b) steps through two iterables in parallel, pairing items at the same position.

fruits = ["apple", "banana", "cherry"]
 
# Basic for loop
for fruit in fruits:
    print(fruit)
 
# range: 0, 1, 2, 3, 4
for i in range(5):
    print(i)
 
# enumerate: index + value
for i, fruit in enumerate(fruits):
    print(i, fruit)   # 0 apple, 1 banana, 2 cherry
 
# zip: iterate two lists in parallel
prices = [1.20, 0.50, 2.00]
for fruit, price in zip(fruits, prices):
    print(f"{fruit}: ${price:.2f}")

while runs as long as its condition is true. Use break to exit early when a goal is reached, and continue to skip the rest of the loop body for the current iteration. A while True: loop with an explicit break is a common pattern for "keep reading until a sentinel value."

# Read lines until the user types "quit"
lines = []
while True:
    line = input("> ")
    if line == "quit":
        break
    lines.append(line)
 
print(f"Collected {len(lines)} line(s)")
 
# continue: skip blank lines
for line in lines:
    if not line.strip():
        continue
    process(line)

Both for and while accept an optional else clause. The else block runs when the loop finishes normally - that is, without hitting a break. It reads as "no early exit occurred." Most teams avoid this because readers unfamiliar with Python expect else to mean "condition was false," not "loop completed." A sentinel variable is usually clearer.

# for/else: runs the else block only when break was never hit
def find(items: list, target: int) -> str:
    for item in items:
        if item == target:
            break
    else:
        return "not found"
    return f"found {target}"
 
find([1, 2, 3], 2)  # 'found 2'
find([1, 2, 3], 9)  # 'not found'
 
# Same logic, clearer with a sentinel variable
def find_v2(items: list, target: int) -> str:
    found = False
    for item in items:
        if item == target:
            found = True
            break
    return f"found {target}" if found else "not found"

In production

The for/else clause runs when the loop exits without break - it reads as "no match found." Most teams use a sentinel variable instead because else on a loop is widely misunderstood and will surprise reviewers unfamiliar with Python. Modifying a list while iterating it (for x in xs: xs.remove(x)) skips elements because the iterator's position shifts after each removal; iterate over a copy (for x in xs[:]:) or build a new list with a comprehension.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free