Nested Loops in Python: build a grid from two counters
A single loop gives you a line. Nest one loop inside another and you get a plane — the classic invader grid, rows by columns, from four lines.
The big idea
In a nested loop, the inner loop completes a full run for every single iteration of the outer loop.
See it in code
First, see the control flow with nothing else in the way. For every value of the outer row, the inner col loop runs to completion before row advances:
for row in range(2):
for col in range(3):
print(f"row {row}, col {col}")row 0, col 0 row 0, col 1 row 0, col 2 row 1, col 0 row 1, col 1 row 1, col 2
Two outer steps × three inner steps = six lines. The inner loop resets and runs in full every time the outer loop ticks over — that multiplication is the whole point.
Now let both counters do math. col drives the x-coordinate and row drives the y — so each (row, col) pair maps to a distinct point on the plane:
for row in range(2):
for col in range(3):
print("enemy at", 60 + col * 90, 70 + row * 70)enemy at 60 70 enemy at 150 70 enemy at 240 70 enemy at 60 140 enemy at 150 140 enemy at 240 140
The x's sweep across while y holds, then y drops for the next row. Feed those numbers to real sprites and the printout becomes a formation.
Same nested loop, now spawning enemies. col sets x, row sets y — exactly the mapping from the warm-up — so together the two counters cover the whole plane:
from game import Stage, Enemy
screen = Stage.create()
Stage.clear(screen)
for row in range(3):
for col in range(5):
Enemy(60 + col * 90, 70 + row * 70).draw(screen)
The inner loop runs 5 times for each of the outer loop's 3 rows: 3 × 5 = 15 enemies. Swap which counter drives x and which drives y, and the grid rotates — order decides rows versus columns.
Two nested loops walk any 2D structure: every cell of a spreadsheet, each pixel (x, y) of an image, all pairs in a list for a distance check, a multiplication table. Nest a third loop and you're in 3D. The cost also multiplies — 3 × 5 is fine, but a nested loop over a big list is n² work, which matters as data grows.
Try it yourself
Add row-by-row color or spacing by using row in more of the math. Then write a nested loop that prints every (row, col) pair, so you can watch the inner loop finish before the outer advances.
The common mistake
Reusing the same counter name for both loops, or forgetting one in the body. If both loops use i, the inner one overwrites the outer's value and the grid collapses. Give them distinct names (row, col) and use both in the position.
What it unlocks
Grids extend for loops and range(), and build the formations behind enemy waves and tile-based art.