Skip to content
Python Basics: Numbering and Pairing Items with enumerate and zip

Python Basics: Numbering and Pairing Items with enumerate and zip

October 20, 2026

A loop often needs a human-friendly row number or matching values from two columns. enumerate and zip express those jobs directly. Use strict pairing when unequal column lengths would hide missing data.

These three standalone examples were tested on Linux with Python 3.12.14. They require no third-party packages. Assertions check the results; run without Python’s -O option.

Prepare a scratch directory

You need Bash, Python 3.12 or newer on your PATH, a text editor, and the mktemp, rm, and rmdir utilities. Keep using the same terminal and save each named source file as UTF-8.

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

1. Number a checklist from one

Save as number_checklist.py:

tasks = ["lint", "test", "build"]
numbered = []
for number, task in enumerate(tasks, start=1):
    numbered.append(f"{number}. {task}")

assert numbered == ["1. lint", "2. test", "3. build"]
assert list(enumerate([], start=1)) == []
for line in numbered:
    print(line)

Run it:

python3 number_checklist.py

Output:

1. lint
2. test
3. build

enumerate supplies a counter alongside each value. start=1 is useful for display; it does not skip the first item. The empty checklist produces no numbered rows.

2. Pair equal-length columns

Save as pair_sensor_readings.py:

locations = ["north", "south"]
temperatures = [18, 21]
paired = list(zip(locations, temperatures, strict=True))

assert paired == [("north", 18), ("south", 21)]
assert list(zip([], [], strict=True)) == []
for location, temperature in paired:
    print(f"{location}: {temperature} C")

Run it:

python3 pair_sensor_readings.py

Output:

north: 18 C
south: 21 C

zip takes one item from each iterable to form each tuple. strict=True requires matching lengths. Two empty inputs match and produce an empty result. This pairs by position; it does not join records by a shared identifier.

3. Validate the whole pairing before using the rows

Save as reject_unequal_columns.py:

names = ["first", "second"]
amounts = [10]
published = []
pairs = zip(names, amounts, strict=True)
print("Pairing iterator created")

try:
    rows = list(pairs)
except ValueError:
    print("Unequal columns rejected")
else:
    published.extend(rows)

assert published == []
print("Published rows:", published)
truncated = list(zip(names, amounts))
assert truncated == [("first", 10)]
print("Default zip:", truncated)

Run it:

python3 reject_unequal_columns.py

Output:

Pairing iterator created
Unequal columns rejected
Published rows: []
Default zip: [('first', 10)]

zip is lazy: construction succeeds, then consuming the mismatched inputs raises ValueError. The default zip silently stops at the shorter input. Materializing these small, finite inputs before publishing prevents partial downstream changes. A loop that publishes each pair immediately could act on earlier rows before discovering the mismatch.

When to use this

Use enumerate for counters and strict zip for aligned columns. Materializing results costs memory, and strict zip does not roll back consumption of its inputs. For large streams, design validation and downstream error handling together.

References

Clean up

In the same terminal, remove only these example files, then return to your original directory and remove the empty scratch directory:

rm -- number_checklist.py pair_sensor_readings.py reject_unequal_columns.py
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

Acknowledgements

This post was created with assistance from AI.

Last updated on