What a nested loop is
A nested loop is a loop inside another loop. The outer loop starts an iteration, the inner loop runs from start to finish, and only then does the outer loop move on to its next iteration. Each time the outer loop comes around, the inner loop begins again from scratch.
That makes nested loops a natural fit for anything with two dimensions: rows and columns of a table, a grid of characters, a list of lists, or every pair of items in a list.
for i in range(1, 4):
for j in range(1, 4):
print(i * j, end=" ")
print()
1 2 3
2 4 6
3 6 9
The body of the inner loop runs once for every combination of i and j: 3 times 3, which is 9 times. In general, an outer loop of a passes with an inner loop of b passes runs the inner body a * b times.
Printing a rectangle of stars
The lesson's exercise is to print a rectangle of 5 rows and 10 columns made of *. The outer loop counts rows and the inner loop counts the stars in one row.
- Write the outer loop:
for row in range(5)gives five passes, one per row. - Write the inner loop inside it:
for col in range(10)gives ten passes per row. Altogether the star is printed 50 times. - Print one star without a newline:
print('*', end=''). By defaultprintends its output with a newline, and theendparameter replaces that with whatever you pass, here an empty string. The ten stars of a row then stay on one line. - Finish the row: after the inner loop, still inside the outer loop, call
print()with no arguments. It prints just a newline, so the next row starts on a fresh line.
for row in range(5):
for col in range(10):
print('*', end ='')
print()
The output is five lines of ten stars each:
**********
**********
**********
**********
**********
The indentation of that last print() decides everything. It is level with the inner for, so it belongs to the outer loop's body and runs once per row. If you indent it one level deeper, it runs after every star, and you get a column of 50 single stars instead of a rectangle. If you remove one level, it runs once after all the loops and the rows are not separated.
Looping over a list of lists
A two-dimensional list is a list whose items are lists. The outer loop visits the rows, and the inner loop visits the values in one row:
arr = [[1, 2], [3, 4], [5, 6]]
for i in range(len(arr)):
for j in range(len(arr[i])):
print(arr[i][j], end=" ")
1 2 3 4 5 6
The inner loop uses len(arr[i]) because arr[i] is itself a list, and rows are allowed to have different lengths. When you only need the values and not the indices, you can loop over the items directly, which is shorter and the usual Python style:
for row in arr:
for value in row:
print(value, end=" ")
How break works in a nested loop
A break ends only the loop it is written in. This is the most common surprise with nested loops. With break in the inner loop, the outer loop carries on:
arr = [[1, 2], [3, 4], [5, 6]]
for i in range(len(arr)):
for j in range(len(arr[i])):
if arr[i][j] == 3:
break
print(arr[i][j], end=" ")
1 2 5 6
When i is 1 and j is 0, the value is 3 and the inner loop stops, which skips the 4 as well. The outer loop then continues with i = 2 and prints the whole last row.
With break in the outer loop, all the looping stops:
for i in range(len(arr)):
if arr[i][0] == 3:
break
for j in range(len(arr[i])):
print(arr[i][j], end=" ")
1 2
Leaving both loops at once
Python has no statement that breaks out of two loops. When you search a grid and want to stop at the first match, the cleanest way is to put the loops in a function and return:
def find(arr, target):
for i, row in enumerate(arr):
for j, value in enumerate(row):
if value == target:
return i, j
return None
print(find([[1, 2], [3, 4], [5, 6]], 4)) # (1, 1)
print(find([[1, 2], [3, 4], [5, 6]], 9)) # None
The other option is a flag variable that the outer loop checks after the inner loop ends, followed by its own break. continue follows the same rule as break: it skips to the next iteration of the innermost loop only.
Common mistakes
- Reusing the loop variable. If both loops use
i, the inner loop overwrites the outer one. Use different names such asrowandcol. - Wrong indentation for
print(). It decides whether a newline is printed per star, per row or once. Mixed tabs and spaces in one block can also make Python reject the file with anIndentationError; use four spaces per level. - Forgetting
end=''. Without it every star lands on its own line. - Using
len(arr)for the inner loop. That is the number of rows, not the length of the current row. - Ignoring the cost. The inner body runs
a * btimes, so two loops over the same list of 1,000 items run the body a million times. Brute force solutions to interview problems are built from exactly this pattern, and the next lessons use it to check every pair of elements.
Key takeaways
- The inner loop completes all its iterations for each single iteration of the outer loop.
- The inner body runs (outer passes) times (inner passes).
print(x, end='')prints without a newline, and an emptyprint()ends the row.breakandcontinueaffect only the loop that contains them.- To stop both loops, return from a function or use a flag.
