Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner ~30 min· topic 7 of 9

Topic 2.7

Nested Loops and Patterns

In one line

A loop inside a loop runs the inner loop completely for every pass of the outer loop, so the total work is rows times columns. Nested loops draw grids and tables, and leaving both loops at once needs a flag, a for/else, or (later) a function.

Think of it like this

A clock. For every hour, the minute hand goes all the way around: 60 minutes. Then the hour moves on, and the minute hand goes round again. The outer loop (hours) moves slowly; the inner loop (minutes) runs through all its values for each single step of the outer one.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Nested loop
A loop inside another loop.
Outer loop / inner loop
The outer loop runs the slow, outer cycle; the inner loop runs completely on every outer pass.
Grid
Data laid out in rows and columns, like a table or a chessboard.
Row / column
A horizontal line of cells / a vertical line of cells.
end=""
An option of print that makes the next print continue on the same line.
Flag
A variable such as found that records something happened, so another loop can react to it.
Time cost n × m
The number of steps of two nested loops: the outer count times the inner count.
Pattern
A shape drawn with text, such as a triangle of stars.

Step by step

01The inner loop runs completely each time

Trace it with your finger. row is 1: the inner loop runs col = 1, 2, 3 and prints three numbers. Then row is 2 and the inner loop runs all three columns again. The line print(row, col) runs 3 × 3 = 9 times.

main.pywhole filepython
for row in range(1, 4):
    for col in range(1, 4):
        print(row, col)
The inner loop runs completely each timediagram
Rendering diagram…

02A multiplication table

The inner print ends with end="" so the numbers stay on one line. The format :3 pads each number to a width of 3 so the columns line up (Topic 1.9). The bare print() after the inner loop moves to the next line.

main.pywhole filepython
for row in range(1, 4):
    for col in range(1, 4):
        print(f"{row * col:3}", end="")
    print()

03A pattern without an inner loop

A triangle needs only one loop, because "stars repeated size times" is "*" * size. Always ask whether the inner loop is really needed.

main.pywhole filepython
for size in range(1, 5):
    print("*" * size)

04break only leaves the inner loop

When j reaches 1 the inner loop breaks, but the outer loop carries on with the next i. That is why "pair 0 0", "pair 1 0" and "pair 2 0" all appear.

main.pywhole filepython
for i in range(3):
    for j in range(3):
        if j == 1:
            break
        print("pair", i, j)

05Leaving both loops with a flag

Search a grid (a list of lists: each row is itself a list) for the value 5. When the inner loop finds it, it records the position as a pair such as (1, 1) and breaks. The outer loop sees that found is no longer None and breaks too. enumerate supplies row and column numbers (Topic 2.6).

main.pywhole filepython
grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
target = 5
found = None
for r, row in enumerate(grid):
    for c, value in enumerate(row):
        if value == target:
            found = (r, c)
            break
    if found is not None:
        break
print("Found at", found)       # Found at (1, 1)

06Counting the cost

Count the steps instead of imagining them. Three outer passes times four inner passes is twelve. If both sizes double, the steps quadruple.

main.pywhole filepython
steps = 0
for a in range(1, 4):
    for b in range(1, 5):
        steps += 1
print("steps:", steps)     # 12

07A naming trap

If both loops use the same variable, the inner loop overwrites the outer one's variable on every pass. Python allows it without complaint, so give each loop its own name.

main.pywhole filepython
for i in range(2):
    for i in range(3):      # reuses i: confusing
        pass
    print("outer i is now", i)     # 2, not 0 or 1

Try it yourself

  1. 1

    Predict the count

    How many times does print("x") run in for i in range(4): with for j in range(5): inside? Write down the number, then run it with a counter to check (20).

  2. 2

    Draw a rectangle

    Print a rectangle of # that is 3 rows high and 5 columns wide, using the inner loop with end="". Then do it again with only one loop and string repetition.

  3. 3

    Staircase

    Print the numbers 1, 12, 123, 1234 on separate lines (each row longer than the one before). Hint: the outer loop picks the row number, the inner loop goes from 1 up to that number and prints with end="", and a bare print() ends each row.

Code & diagrams

A table and a triangle New tab

Change both ranges to range(1, 6) for a 5 by 5 table. Then try f"{row * col:4}" to widen the columns.

Sign in to run this example in your browser.

Expected output

  1  2  3
  2  4  6
  3  6  9
*
**
***
****
Breaking out, and counting the steps New tab

Change target to 9 and see the new position. Change it to 100: what does found stay as?

Sign in to run this example in your browser.

Expected output

Found at (1, 1)
pair 0 0
pair 1 0
pair 2 0
steps: 12

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Expect break to leave both loops

Write a break in the inner loop and expect the whole search to stop.

main.pywhole filepython
for i in range(3):
    for j in range(3):
        if j == 1:
            break
        print("pair", i, j)
terminal
$ python main.py
── what you'll see ──
pair 0 0
pair 1 0
pair 2 0

Myth vs fact

Myth

Nested loops are always slow.

Fact

They cost n × m steps, which is fine for small grids and a problem only for large ones. Check the sizes before you worry.

Myth

break in the inner loop stops everything.

Fact

It leaves only the nearest loop. The outer loop continues.

Myth

You need an inner loop to repeat a character.

Fact

String repetition ("*" * 5) does it with no loop.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Total work grows by multiplication, so deeply nested loops over large data are the first thing to look for when a program is slow. Real fixes are a better data structure (a set or dict lookup replaces an inner loop, Topic 4.11), vectorising with NumPy (Topic 14.3), or restructuring the algorithm. The DSA course (/dsa) is built around this idea.

  • ▸

    itertools.product(a, b) (Topic 9.4) replaces two nested for loops with one, producing every pair, which flattens deep nesting. It does the same number of steps but reads better.

  • ▸

    An inner for/else followed by continue, with a break after it in the outer loop, is a known way to break out of two loops without a flag, but a function with a return is clearer to most readers and is what style guides recommend.

  • ▸

    When you build a grid, write [[0] * cols for _ in range(rows)]. [[0] * cols] * rows repeats one inner list rows times, so changing one cell changes a whole column (Topic 4.9).

  • ▸

    In AI code, nested Python loops over numbers are a red flag. Matrix and batch maths belongs in NumPy or PyTorch, which run the inner loop in compiled code and are often 100 times faster than the same loops written in plain Python.

Remember this

  1. 1

    A nested loop is a loop whose body contains another loop. Python runs the first outer pass, then the whole inner loop from start to end, then the second outer pass, then the whole inner loop again, and so on. If the outer loop makes 3 passes and the inner makes 4, the innermost line runs 3 × 4 = 12 times.

  2. 2

    That multiplication is the cost of nesting. Total work is n × m: a 1,000 by 1,000 grid is a million steps. Three nested loops multiply three numbers. When your data gets big, extra levels of nesting are what make a program slow (Topic 4.11 explains Big-O thinking, and the DSA course builds on it).

  3. 3

    Nested loops are the natural tool for anything shaped like a grid: rows and columns in a table, cells on a board, pixels in an image, every pair of items from two lists. A multiplication table is the classic example: the outer loop picks the row, the inner loop walks the columns.

  4. 4

    To print a row on one line, use print(..., end="") inside the inner loop (so it does not start a new line each time), and a bare print() after the inner loop to finish the row. Text patterns such as a triangle can do without an inner loop at all, using string repetition: "*" * size.

  5. 5

    break and continue apply to the inner loop only (Topic 2.5). To stop both loops, you have three choices. Use a flag (a variable such as found that the outer loop checks). Or use the inner loop's else with a continue/break pair (an advanced trick). Or, best of all, put the loops in a function and return (Topic 5.10).

  6. 6

    Do not reuse the same variable name (i) for both loops. It makes the outer variable misleading.

  7. 7

    Data in real programs is often nested too: a list of lists is a grid, and a list of dictionaries is a table. A loop over the rows with an inner loop over the cells is how you process them (Topic 4.8). The same shape appears in AI work: a batch of examples (outer) each with many numbers (inner), which NumPy later does for you in one step.

Explain it without notes

01

How many times does the innermost statement run in two nested loops of sizes n and m?

02

How do you print a grid with each row on its own line?

03

How can you exit two nested loops at once?

04

Why can reusing the same loop variable in nested loops cause bugs?

Practice

01

Print a rectangle of # 3 rows high and 5 columns wide using two loops.

02

Print every pair (i, j) with i and j from 0 to 1, as 0 0, 0 1, 1 0, 1 1, one per line.

03

Count how many steps a loop of 5 outer and 4 inner passes makes and print Steps: 20.

04

In numbers = [1, 4, 6, 9], find the first pair a < b (checking a from the list in order, then b in order) whose sum is 10 and print Found: (1, 9). Stop both loops.

Trade-offs

  • ↔

    Nested loops vs a better data structure. Two loops are the simplest correct solution, but a set or dict often removes the inner loop and the n × m cost.

  • ↔

    Flag vs function with return. A flag works anywhere. A function with return is clearer but needs the function you will learn in Phase 5.

Done when you can

  • Done when you can trace a nested loop by hand and say how many times the inner line runs.

  • Done when you can print a grid using end="" and a bare print().

  • Done when you can explain which loop a break leaves and exit both loops with a flag.

  • Done when you can state the n × m cost and why same-name loop variables are risky.