Sometimes a single loop is not enough. When you need to do something repeatedly within each repetition — like filling every cell in a grid, or checking every item against every other item — you need a nested loop: a loop inside a loop.
Basic Structure
for outer in range(3):
for inner in range(3):
print(f"({outer}, {inner})")
Output:
(0, 0)
(0, 1)
(0, 2)
(1, 0)
(1, 1)
(1, 2)
(2, 0)
(2, 1)
(2, 2)
For each single iteration of the outer loop, the inner loop runs all the way through before the outer loop moves to its next step. With range(3) for both, the inner body runs 3 × 3 = 9 times in total.
Always use different variable names for the outer and inner loop counters. Using i for both is a common beginner mistake that causes very confusing bugs.
Printing a Multiplication Table
Nested loops are perfect for generating structured output like a table:
for row in range(1, 6):
for col in range(1, 6):
result = row * col
print(f"{result:4}", end="")
print() # new line after each row
Output:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
Nested Loops with Lists
Nested loops are also used when each item in a list is itself a list (a 2D structure):
grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
for row in grid:
for cell in row:
print(cell, end=" ")
print()
Output:
1 2 3
4 5 6
7 8 9
Think of a nested loop like a clock: the inner loop (seconds hand) completes a full cycle for every single tick of the outer loop (minutes hand).
Counting Iterations
Before writing a nested loop, it is worth estimating how many times the body will run. For two nested loops with n and m iterations respectively, the body runs n × m times.
| Outer range | Inner range | Total iterations |
|---|---|---|
| 5 | 5 | 25 |
| 10 | 10 | 100 |
| 100 | 100 | 10,000 |
For very large values this can make programs slow, which connects to ideas in time complexity.
Deeply nested loops (three or more levels) are usually a sign that your code could be reorganised. They can make programs very slow and difficult to read. If you find yourself writing three nested loops, step back and consider whether there is a smarter approach.
A Practical Example: Checking Pairs
numbers = [2, 5, 8, 11]
target = 13
for i in range(len(numbers)):
for j in range(i + 1, len(numbers)):
if numbers[i] + numbers[j] == target:
print(f"{numbers[i]} + {numbers[j]} = {target}")
Output:
2 + 11 = 13
5 + 8 = 13
This checks every unique pair of numbers to find which ones add up to the target — a task that requires a nested loop because each number must be compared against all others.
Practice Problems
How many times will print('hello') run in this code? for i in range(4): for j in range(3): print('hello')
Show answer
Write nested loops that print a 3×3 grid of asterisks (each row on its own line).