Nested for Loops
Nothing new to learn here — no keyword, no syntax. A nested loop is a loop written inside the body of another loop, and the whole lesson is one fact: the inner loop runs all the way through, from the beginning, on every single round of the outer one.
1Why put a loop inside a loop?
One loop prints the table of 7. What prints the tables of 2, 3 and 4? You need to repeat the whole table — and the table is itself a repetition. A repetition of a repetition is a nested loop.
Anything with rows and columns is the same shape: a seating plan, a calendar month, a pattern of stars, marks for every student in every subject. The outer loop takes the rows; the inner loop takes what is in one row.
2How it is written
for i in range(1, 4): <- outer header
for j in range(1, 5): <- inner header: indented, so it IS the outer body
print(i, j) <- inner body: indented twice
print('row done') <- outer body again, after the inner loop ends
print('all done') <- at the margin: outside both loopsThe indentation is doing all the work, exactly as it has since the if statement — only now there are two levels of it. Four spaces means “inside the outer loop”. Eight means “inside the inner loop”. Back at the margin means outside both.
And each loop needs its own loop variable. The outer one is traditionally i and the inner one j. Using i for both would mean the inner loop overwriting the outer loop's value on every round — which is not an error, and does not do what anybody wants.
3Watch both counters
Step through it and watch j go all the way from 1 to 4 before i moves at all — and then start again from 1.
3 outer rounds, 4 inner rounds each. Count the cells as they fill.
Three rows, four cells each. 3 × 4 = 12, not 3 + 4 — the inner loop starts again from the beginning on every row.
Nothing has run yet. There are two loops here: the outer one counts i through 1, 2, 3 and the inner one counts j through 1, 2, 3, 4.
4Three tables from one program
The outer loop picks the table; the inner loop prints the rows of it. The print() calls between them are worth studying — where a statement sits decides how often it happens:
# the tables of 2, 3 and 4 — a loop inside a loop
for n in range(2, 5):
print('Table of', n)
for i in range(1, 4):
print(n, 'x', i, '=', n * i)
print()Table of 2 2 x 1 = 2 2 x 2 = 4 2 x 3 = 6 Table of 3 3 x 1 = 3 3 x 2 = 6 3 x 3 = 9 Table of 4 4 x 1 = 4 4 x 2 = 8 4 x 3 = 12
print('Table of', n) is in the outer body, so it runs three times — once per table. The multiplication line is in the inner body, so it runs nine times. And the empty print() at the end of the outer body puts a blank line after each table, which is why the output has three blocks rather than one wall of numbers.
print('Table of', n) by four more spaces and it joins the inner loop — printing the heading before every single row, nine times. Nothing about that is an error, so nothing will tell you.5Counting the rounds
Exam questions ask this constantly, and the counting is simple once you stop adding:
# how many times does the inner body run?
count = 0
for i in range(1, 4):
for j in range(1, 5):
count = count + 1
print('The outer body ran 3 times')
print('The inner body ran', count, 'times')The outer body ran 3 times The inner body ran 12 times
Three values in the outer range, four in the inner: 3 × 4 = 12. Multiply, do not add — because the inner loop starts over from its first value on each round of the outer, rather than carrying on from where it stopped.
6When the inner loop depends on the outer one
The inner range does not have to be a fixed set of numbers. It can be built out of the outer loop variable — and then each row is a different length. That is how patterns are made:
# a triangle of stars, built one row at a time
for row in range(1, 5):
line = ''
for star in range(1, row + 1):
line = line + '*'
print(line)* ** *** ****
On the first round row is 1, so the inner range is range(1, 2) — one star. On the fourth it is range(1, 5) — four. The inner loop is not counting to a fixed number; it is counting to whatever the outer round is.
Note also where line lives. It is created inside the outer body but outside the inner one: emptied at the start of each row, added to by every star in that row, and printed once the row is complete. Above the outer loop it would collect every star in the triangle into one line; inside the inner loop it would be emptied after each star and only ever print one.
print() moves to a new line every time, so printing stars one at a time would give a vertical column. There is a way to tell print() not to — a later lesson — so for now the row is built up with + and printed once, which works everywhere and makes the row visible as a value.7Try it
Run it, then change range(1, 5) to range(1, 8). Then try moving the print(line) in by four spaces and see the triangle grow a step at a time.
8Recap
for i in range(1, 4): / for j in range(1, 5): / print(i, j) — how many lines are printed?
In the tables program, what happens if print('Table of', n) is indented to sit inside the inner loop?
In the star triangle, why is line = '' inside the outer loop rather than above it?