Chapter 3 · Junior Developer

enumerate and building lists in a loop

Three loop patterns you'll use every day.

enumerate gives you the index *and* the value together, so you don't manage a separate counter. for i, value in enumerate(items): yields (0, first), (1, second), … Pass start=1 to count from 1 instead of 0 — handy for human-facing line numbers.

Build-a-list-in-a-loop: start with an empty result = [] *before* the loop, then result.append(...) each pass. After the loop, result holds everything you collected. (Initialise the accumulator outside the loop, or it resets every pass.)

Filter as you build: combine the two — only append items that pass a test. An if inside the loop decides what makes the cut, so the result contains just the matches. This is the manual version of what a list comprehension does in one line (coming up later) — and the version to reach for when the logic is more than a single expression.

Syntax

for i, value in enumerate(items):        # i = 0, 1, 2, ...
    ...
for i, value in enumerate(items, start=1):  # i = 1, 2, 3, ...
    ...

# Build (and filter) a list:
result = []                 # before the loop
for item in items:
    if keep(item):          # optional filter
        result.append(item)
return result

Worked examples

enumerate with start=1 for 1-based numbering

def number_lines(lines):
    result = []
    for i, line in enumerate(lines, start=1):
        result.append(f"{i}: {line}")
    return result

print(number_lines(["alpha", "beta"]))
# ['1: alpha', '2: beta']

Filter while building — keep only the matches

def collect_errors(events):
    result = []
    for event in events:
        if "ERROR" in event:
            result.append(event)
    return result

print(collect_errors(["OK", "ERROR bad", "OK", "ERROR timeout"]))
# ['ERROR bad', 'ERROR timeout']

Accumulate with a filter (sum only the positives)

def sum_positives(numbers):
    total = 0
    for n in numbers:
        if n > 0:
            total += n
    return total

print(sum_positives([3, -1, 0, 7, -2]))   # 10

Common mistakes & gotchas

Initialising the accumulator inside the loop

result = [] (or total = 0) must sit *before* the loop. Put it inside and it resets every pass, leaving you with only the last item. The accumulator is set up once, then updated each iteration.

Using range(len(items)) instead of enumerate

for i in range(len(items)): item = items[i] works but is clumsy and easy to get wrong. for i, item in enumerate(items): gives you both cleanly — it's the Pythonic way.

Forgetting start= when you want 1-based numbers

enumerate(items) counts from 0. If you're labelling lines or ranks for humans, you usually want enumerate(items, start=1), or you'll be off by one.

Why it matters

Reading a collection, transforming or filtering it, and collecting the results is the bread and butter of backend work — processing log lines, request payloads, query rows. `enumerate` and the build-a-list pattern are the readable, idiomatic way to do it, and they're the mental model that list comprehensions compress.