Course outline · 0% complete

0/32 lessons0%

Course overview →

Nested Loops

lesson 5-4 · ~10 min · 16/32

A loop inside a loop

Plenty of real data has two dimensions, rows and columns: an image is rows of pixels, a spreadsheet is rows of cells, a schedule is days by hours. To visit every cell you put a loop inside a loop. The outer loop picks a row, and for each single row the inner loop runs completely, visiting every column, before the outer loop moves to the next row.

for row in range(1, 4):
    for col in range(1, 4):
        print(row, col)

The inner body runs 3 × 3 = 9 times. That multiplication is the key fact about nesting: total work is the product of the two loop sizes. It is also the standard reason a program is slow, a nested loop over two big collections multiplies into millions of passes, which is why interviewers probe it.

outer loop picks a row 1 2 3 inner loop runs fully for each row 1 2 3 2 4 6 3 6 9 then --- divider Row 1 finishes all three columns before row 2 starts. Total inner passes is the PRODUCT of the sizes: 3 x 3 = 9 The divider sits in the outer body, so it prints 3 times, not 9. Indentation decides which loop owns a line.
How a nested loop covers a grid. The outer loop selects row 1, 2, or 3 and the inner loop runs completely for that row, filling every column before the outer loop advances, so the products appear left to right one row at a time. The total number of inner passes is the product of the two loop sizes, three by three giving nine, and the divider line belongs to the outer body so it prints only three times.

The multiplication grid

The order of the output is the thing to study. Row 1 pairs with every column before row 2 begins, and the divider prints once per outer pass because it sits outside the inner loop.

for row in range(1, 4):
    for col in range(1, 4):
        print(f"{row} x {col} = {row * col}")
    print("---")

Output

1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
---
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
---
3 x 1 = 3
3 x 2 = 6
3 x 3 = 9
---

Nine product lines appeared, three per row, which is the 3 by 3 multiplication in action. The inner loop restarts from col at 1 on every outer pass, since range is rebuilt each time the inner for is reached.

Indentation alone decides where the divider goes. Indented one level it belongs to the outer loop and prints three times, and indented two levels it would belong to the inner loop and print nine times instead.

An outer loop running 4 times with an inner loop of 5 passes each executes the inner body 20 times.

The inner loop runs to completion for every single outer pass, so the counts multiply rather than add. Guessing 9 is the classic error, and it comes from reading the two loops as though they ran side by side.

That multiplication is also the practical reason to care. Estimating nested work as the product of the loop sizes is how you tell in advance whether a piece of code will finish instantly or grind, since two loops over a thousand items each mean a million passes.

Not every two-dimensional shape needs two loops. String repetition, met back in lesson 3-2 where "ab" * 3 gave "ababab", builds each row in a single expression.

for row in range(1, 5):
    print("*" * row)

Output

*
**
***
****

The loop variable doubles as the count of stars, so row at 1 produces one star and row at 4 produces four. The range runs to 5 so that 4 is the last value included.

An inner loop appending one star at a time would produce identical output, and it is worth recognizing that the repetition operator is doing that work for you. Letting the language handle the inner dimension keeps the loop body to a single readable line.

This snippet prints the single number 3.

count = 0
for i in range(3):
 for j in range(i):
 count += 1
print(count)

The inner range depends on the outer variable, so it changes size on every pass. With i at 0 the inner range is range(0), which produces nothing at all and adds nothing. With i at 1 there is one addition, and with i at 2 there are two, giving 0 plus 1 plus 2, or 3.

Inner loops sized by the outer variable are common, and they describe a triangle of work rather than a full grid. That is the shape behind comparing every pair in a collection exactly once, where comparing item 3 against item 1 would be redundant after item 1 was compared against item 3.