for loops and range
Iterate over sequences with for, count with range, count backwards with reversed, number items with enumerate, and pair sequences with zip.
What you will learn
- Loop over any iterable with for x in iterable
- Generate number sequences with range, and walk them backwards with reversed
- Use enumerate to get positions and zip to walk two sequences together
- Use zip(strict=True) to catch sequences of different lengths
On this page
A while loop waits for a condition. A for loop is for the other common case: do this for each item. In Python it works on anything iterable: a string, a range of numbers, the lines of a file, a list, and many more. The collections module introduces lists, and a for loop treats them exactly as it treats the strings and ranges in this lesson.
for over a range
for i in range(3): print(i)012for i in range(3): binds the name i to each number in turn and runs the indented block once per number. There is no counter to manage and no condition to get wrong.
range
range produces numbers on demand. It takes the same arguments as a slice:
range(stop)counts from 0 up to, but not including,stop.range(start, stop)begins atstart.range(start, stop, step)moves bystep, which may be negative.
range(3) gives 0, 1, 2: three numbers, which is why it suits “do this 3 times”. A range does not build the whole sequence in memory, so range(1_000_000) is cheap. To see the numbers in one go, wrap it in list(...), which turns any sequence into a list:
print(list(range(4)))print(list(range(2, 10, 3)))print(list(range(5, 0, -2)))[0, 1, 2, 3][2, 5, 8][5, 3, 1]for over a string
A string is a sequence of characters, so for can visit one character at a time.
for letter in "abc": print(letter.upper())ABCNested loops
The inner loop runs completely for every pass of the outer one. This prints a small multiplication table, building each row as a string:
for row in range(1, 4): line = "" for col in range(1, 6): line += f"{row * col:3}" print(line) 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15Going backwards: reversed
reversed(sequence) walks a sequence from the end to the start without changing it. It works on ranges, strings, and the lists you will meet soon.
for n in reversed(range(1, 4)): print(n)print("Liftoff")
for letter in reversed("abc"): print(letter, end=" ")print()321Liftoffc b areversed(range(1, 4)) is easier to read than range(3, 0, -1), although they give the same numbers.
enumerate
If you need the position as well as the item, do not write range(len(items)). Use enumerate, which yields (position, item) pairs. The optional start=1 begins counting at 1, which suits numbered lists shown to people.
for position, letter in enumerate("abc", start=1): print(position, letter)1 a2 b3 cThe two names before in receive the two halves of each pair. The tuples lesson explains this unpacking properly.
zip
zip walks several sequences in step and yields one pair per position:
for letter, digit in zip("abc", "123"): print(letter, digit)a 1b 2c 3By default zip stops silently at the shortest input. The c simply vanishes below, with no warning:
for letter, digit in zip("abc", "12"): print(letter, digit)a 1b 2Requires version 3.10 or later Passing strict=True makes zip raise a ValueError instead when the lengths differ (PEP 618). Use it whenever two sequences are supposed to line up, so a mismatch becomes a loud error rather than lost data. The error is raised when the shorter input runs out, so the pairs before it are still processed:
for letter, digit in zip("abc", "12", strict=True): print(letter, digit)a 1b 2ValueError: zip() argument 2 is shorter than argument 1Common pitfalls
- Off-by-one with range.
range(1, 10)ends at 9. To include 10, writerange(1, 11). - Changing a sequence while looping over it. Adding or removing items from the list you are iterating makes the loop skip items. Loop over a copy or build a new list.
for i in range(len(items))just to read items. Loop over the items themselves, or useenumeratewhen you need positions.- Reusing the loop variable. After the loop, the name still holds the last item. It does not disappear, and it can clash with a name you meant to use elsewhere.
- Silent truncation with
zip. Withoutstrict=True, mismatched lengths lose data without a message. - Expecting
rangeto include the stop value. It never does, just like slices.
Add up the squares of the numbers 1 to 5 with a for loop and print the total.
Input: none. Expected output: 55.
Show solution
total = 0for n in range(1, 6): total += n * nprint(total)55range(1, 6) gives 1 to 5, since the stop value is excluded. total starts at 0 and grows by the square of each number: 1 + 4 + 9 + 16 + 25.
Print the numbers from 5 down to 1, one per line, using reversed and range, then print Liftoff.
Input: none. Expected output: 5, 4, 3, 2, 1, Liftoff, on six lines.
Show solution
for n in reversed(range(1, 6)): print(n)print("Liftoff")54321Liftoffrange(1, 6) holds 1 to 5 and reversed walks it backwards. The final print is not indented, so it runs once after the loop.
Print a multiplication table for 1 to 4, with row and column headings, using nested loops. Each number is right-aligned in a field 4 characters wide, and the row headings are 2 wide. Use print(..., end="") to stay on a line and an empty print() to finish it.
Input: none. Expected output:
1 2 3 4 1 1 2 3 4 2 2 4 6 8 3 3 6 9 12 4 4 8 12 16Show solution
size = 4
print(" ", end="")for col in range(1, size + 1): print(f"{col:4}", end="")print()
for row in range(1, size + 1): print(f"{row:2} ", end="") for col in range(1, size + 1): print(f"{row * col:4}", end="") print() 1 2 3 4 1 1 2 3 4 2 2 4 6 8 3 3 6 9 12 4 4 8 12 16The first loop prints the heading row. In the main loop, the outer range picks the row, the inner loop prints one cell per column, and the bare print() ends the line. The format spec :4 pads each number to four characters so the columns line up.
Summary
for item in iterable:visits each item in turn;range(start, stop, step)generates numbers and never includesstop.- Use
reversedto walk backwards,enumeratefor positions andzipfor several sequences at once. - Requires version 3.10 or later
zip(..., strict=True)raisesValueErrorwhen lengths differ.
Next: loop control and loop else.