Learning LibraryCore Coding LibraryTeens

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

1The basics

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:

python
for row in range(2):
    for col in range(3):
        print(f"row {row}, col {col}")
Run it — the inner loop completes fully for each outer step:
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.

2A step further

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:

python
for row in range(2):
    for col in range(3):
        print("enemy at", 60 + col * 90, 70 + row * 70)
Run it — every pair becomes an (x, y) position:
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.

3In our world

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:

python
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)
Run it — 3 rows times 5 columns fills a 15-enemy formation:
A 3-row by 5-column formation of fifteen purple enemy sprites filling the upper half of a dark play area.

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.

The same idea, everywhere

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 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.

Want the simpler version? Read the Kids version →