Computer Programming 1 (Python) • Control Flow

Nested Loops — I can use nested loops at an introductory level to process grids or build simple patterns.

Learn how placing one loop inside another lets you process grids, build patterns, and tackle two-dimensional problems in Python.

A Python coding terminal with a program flow and laptop.
Inside this lesson 10 sections 5 questions 6 figures

SECTION 1

Historical Context & Motivation

From the very beginning of computing, programmers needed ways to repeat instructions. A single loop handles one-dimensional repetition — scanning through a list, counting up to a number, or reading lines from a file. But the real world is often two-dimensional: think of a chessboard, a spreadsheet, or pixels on a screen. To process every cell in a grid, early computer scientists realized they needed a loop running inside another loop — a nested loop.

1843 • Ada Lovelace's Algorithm

Ada Lovelace writes notes for Charles Babbage's Analytical Engine, describing iterative loops — the earliest documented concept of a program repeating instructions.

1957 • FORTRAN's DO Loops

IBM releases FORTRAN, the first widely used high-level language. Its DO loop construct makes nested iteration straightforward and popularizes multi-dimensional array processing.

1972 • C Language & Nested for Loops

Dennis Ritchie creates C at Bell Labs. The familiar for-loop syntax (later adopted by Java, JavaScript, and many other languages) makes nested loops a staple of every programmer's toolkit.

1991 • Python's Readable for-in Loops

Guido van Rossum releases Python 0.9.0. Python's indentation-based syntax makes nested loops visually clear because the inner loop must be indented further than the outer loop.

Today, nested loops remain one of the most fundamental patterns in programming. Whether you are rendering graphics, searching a table, or generating multiplication charts, the question is the same: how do you systematically visit every combination of two ranges? That is exactly what nested loops solve.

SECTION 2

Core Principles & Definitions

Before diving into code, let's nail down the key ideas that make nested loops work. If you already know how a single for loop works — iterating over a range of values and executing a body of code each time — then a nested loop is simply putting one loop inside the body of another.

1

Outer Loop

The outer loop controls the first dimension of repetition — for example, which row you are currently on. It runs from start to finish once.
2

Inner Loop

The inner loop controls the second dimension — for example, which column you are processing. It runs completely from start to finish for every single iteration of the outer loop.
3

Total Iterations

The total number of times the innermost code executes equals the product of outer iterations × inner iterations. If the outer loop runs m times and the inner runs n times, the body runs m × n times total.
4

Indentation Matters

In Python, indentation defines which statements belong to which loop. The inner loop's body is indented twice — once for the outer loop and once more for the inner loop.
✦

KEY TAKEAWAY

Think of nested loops like a clock. The minute hand (inner loop) completes a full revolution for every single tick of the hour hand (outer loop). In 12 hours, the hour hand moves 12 positions and the minute hand makes 12 full rotations of 60 ticks each — giving 12 × 60 = 720 total minute ticks. Nested loops work the same way: the inner loop fully completes for every step of the outer loop.

SECTION 3

Visual Explanation — How Nested Loops Traverse a Grid

The diagram below shows how a nested loop visits every cell in a 4-row by 5-column grid. The outer loop variable row steps through rows 0 to 3. For each row, the inner loop variable col steps through columns 0 to 4. The numbered labels inside each cell show the order of execution — notice that the traversal goes left to right across one row before dropping down to the next.

Nested Loop Grid Traversal (4 rows × 5 columns)col →01234row ↓01231234567891011121314151617181920Cells are visited left-to-right, top-to-bottom: 4 rows × 5 columns = 20 total visits
The grid shows execution order (1–20). The outer loop advances the row index, and the inner loop sweeps through every col before the next row begins.

The Python code that produces this traversal pattern is concise. The outer for row in range(4) creates four iterations (rows 0–3). Inside that loop, for col in range(5) creates five iterations (columns 0–4) for each row. The innermost statement — whatever you put at the double-indented level — runs 4 × 5 = 20 times total.

SECTION 4

How Nested Loops Execute — Step by Step

Understanding the exact order in which Python runs each line is critical. Let's trace through a small nested loop to see how the variables change. Consider this snippet:

ℹ

Sample Code

for row in range(3): for col in range(4): print(f"({row},{col})", end=" ") print() # newline after each row

When Python reaches the outer loop, it sets row = 0 and enters the body. The body contains the inner loop, which sets col = 0, prints (0,0), then sets col = 1, prints (0,1), and so on until col = 3. Once the inner loop exhausts its range, Python executes the print() to produce a newline. Then the outer loop increments to row = 1 and the inner loop starts all over again from col = 0.

TOTAL ITERATIONS

Total=outercount×innercountTotal = outer_count × inner_count

If the outer loop runs m times and the inner loop runs n times, the body of the inner loop executes m × n times. For our example: 3 × 4 = 12 prints.

Outer Iteration (row)Inner Iterations (col)Output
row = 0col = 0, 1, 2, 3(0,0) (0,1) (0,2) (0,3)
row = 1col = 0, 1, 2, 3(1,0) (1,1) (1,2) (1,3)
row = 2col = 0, 1, 2, 3(2,0) (2,1) (2,2) (2,3)

Trace table showing variable values during each pass of the nested loop

Notice the key pattern: the inner loop's variable resets to its starting value every time the outer loop advances. This reset behavior is what allows nested loops to systematically cover every combination of row and column values, much like reading every word on every line of a page.

SECTION 5

Building Patterns with Nested Loops

One of the most common introductory exercises for nested loops is building text-based patterns — shapes made of characters like stars, numbers, or letters. These exercises train you to think about which variable controls rows, which controls columns, and how many characters appear on each row. Below is a visual breakdown of three classic patterns and the relationship between the loop variables and the output.

Three Classic Nested Loop PatternsRectangleinner range = constant* * * * ** * * * ** * * * ** * * * *for r in range(4): for c in range(5): print("*",end=" ") print()Rows: 4 | Cols: 5Total: 4 × 5 = 20 starsRight Triangleinner range = row + 1** ** * ** * * *for r in range(4): for c in range(r+1): print("*",end=" ") print()Rows: 4 | Cols: 1,2,3,4Total: 1+2+3+4 = 10 starsNumber Gridprint row × col values1 2 3 42 4 6 83 6 9 124 8 12 16for r in range(1,5): for c in range(1,5): print(r*c,end=" ") print()Rows: 4 | Cols: 4Produces multiplication table
Three patterns showing increasing complexity. The rectangle uses a fixed inner range, the triangle makes the inner range depend on the outer variable, and the number grid uses both variables in the body expression.

The key insight with the triangle pattern is that the inner loop's range depends on the outer loop's variable. When r = 0, the inner loop runs once (range(1)). When r = 3, it runs four times (range(4)). This dependency is what creates the growing shape. Once you understand this mechanism, you can build inverted triangles, diamonds, and other patterns by adjusting the relationship between the outer variable and the inner range.

SECTION 6

Worked Example — Processing a 2D Grid

Suppose you have a 3 × 4 grid of test scores stored as a list of lists (a 2D list). You need to find the highest score in the entire grid. Let's walk through the solution step by step.

ℹ

Given Data

scores = [ [88, 72, 95, 61], [79, 84, 90, 77], [93, 68, 81, 85] ]

Finding the Maximum Score in a 2D List

  1. 1

    Step 1 — Initialize a tracking variable

    Before the loops begin, create a variable to hold the highest score found so far. A safe choice is to set it to the first element: max_score = scores[0][0]. This gives us max_score = 88.
    max_score = 88
  2. 2

    Step 2 — Write the outer loop

    The outer loop iterates over each row. Since scores has 3 sublists, we write for row in scores:. On the first pass, row equals [88, 72, 95, 61].
  3. 3

    Step 3 — Write the inner loop

    Inside the outer loop, we iterate over each individual score: for score in row:. This gives us one score at a time. The inner loop processes all four elements of the current row before the outer loop moves to the next row.
  4. 4

    Step 4 — Compare and update

    In the body of the inner loop, we check: if score > max_score: max_score = score. When the loop reaches 95 (row 0, col 2), max_score updates to 95. It never gets replaced because no later score exceeds 95.
  5. 5

    Step 5 — Output the result

    After both loops finish, print the result: print(f"Highest score: {max_score}"). The loops visited all 3 × 4 = 12 elements, so we are guaranteed to have found the true maximum.
    Highest score: 95
💡

Complete Code

scores = [ [88, 72, 95, 61], [79, 84, 90, 77], [93, 68, 81, 85] ] max_score = scores[0][0] for row in scores: for score in row: if score > max_score: max_score = score print(f"Highest score: {max_score}")

SECTION 7

Strengths, Limitations & Common Pitfalls

Nested loops are powerful, but they come with trade-offs. Knowing when to use them — and when to look for alternatives — is an important part of growing as a programmer.

StrengthsLimitations / Pitfalls
Straightforward way to process grids, tables, and any two-dimensional data structure.Runtime grows multiplicatively. A 1 000 × 1 000 grid means 1 000 000 iterations — which can slow down a program significantly.
Pattern is easy to read in Python thanks to clear indentation.Using the wrong variable (e.g., typing the outer variable where the inner variable should go) is a very common bug that produces wrong results silently.
Can be extended to three or more levels of nesting for higher-dimensional data.Deep nesting (3+ levels) makes code hard to read and maintain. Experienced programmers often refactor into functions.
Flexible — the inner range can depend on the outer variable for triangular or variable-width patterns.Forgetting to reset an accumulator between outer iterations (e.g., a row total) is a frequent logic error.

Weighing the benefits and common mistakes of nested loops

✦

KEY TAKEAWAY

Think of nested loops like searching every seat in a movie theater. You walk down each row (outer loop) and check every seat in that row (inner loop). It's thorough and guarantees you check every seat, but if the theater has 50 rows and 30 seats per row, that's 1 500 seats to inspect. For small grids, this is perfectly fine. For very large data sets, you may eventually learn more efficient strategies.

SECTION 8

Connection to Advanced Topics

Nested loops are your entry point into several more advanced programming concepts. Once you feel confident with two levels of nesting, you'll be ready to explore topics like list comprehensions (a compact Python syntax for building lists with nested iteration), two-dimensional arrays in data science libraries like NumPy, and algorithms that rely on nested iteration such as sorting algorithms (bubble sort, selection sort) and matrix operations.

What You Know NowWhere It Leads
Nested for loops to traverse a 2D listList comprehensions with nested expressions for concise data transformation
Printing star patterns row by rowGenerating pixel art or manipulating image data pixel by pixel
Counting total iterations (m × n)Analyzing algorithm efficiency using Big-O notation (O(n²))
Processing a grid of scoresWorking with Pandas DataFrames, NumPy arrays, and database query results

How nested loop skills connect to future CS topics

You don't need to worry about these advanced topics right now. The important thing is that the mental model you build today — understanding how an inner loop fully completes for each step of an outer loop — transfers directly to every one of these future concepts. Master it here, and those later topics will feel much more approachable.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain in your own words why the inner loop fully completes before the outer loop advances to its next value. Use the clock analogy (or another analogy of your choice) to support your explanation.
Show answer
When Python enters the body of the outer loop, it encounters the inner loop and must execute it completely before it can proceed to the next line of the outer loop's body — and eventually back to the top of the outer loop for the next iteration. This is just like a clock: the minute hand must sweep through all 60 minutes before the hour hand moves forward by one position. The outer loop doesn't 'know' to advance until everything inside it (including the entire inner loop) has finished running.

Question 2: BASIC CALCULATION

How many times does the word "hello" get printed by the following code? Show your calculation. for i in range(6): for j in range(3): print("hello")
Show answer
The outer loop runs 6 times (i = 0, 1, 2, 3, 4, 5) and the inner loop runs 3 times (j = 0, 1, 2) for each outer iteration. Using the formula Total = outer_count × inner_count, we get 6 × 3 = 18. The word "hello" is printed 18 times. Each time the outer loop starts a new iteration, the inner loop resets j back to 0 and runs through all three of its values again.

Question 3: INTERMEDIATE

Write a nested loop that prints the following right triangle pattern (5 rows). Each row r (starting from 1) should contain r stars. * * *
Show answer
The key is making the inner loop's range depend on the outer variable. Here is the code: for r in range(1, 6): for c in range(r): print("*", end=" ") print() When r = 1, range(r) produces range(1), so one star prints. When r = 5, range(5) produces five iterations, so five stars print. The print() after the inner loop moves to a new line. The total stars printed are 1 + 2 + 3 + 4 + 5 = 15.

Question 4: APPLIED

You have a 2D list representing a seating chart where True means the seat is taken and False means it is empty: seats = [ [True, False, True, True], [False, False, True, False], [True, True, True, False] ] Write a nested loop that counts the number of empty seats and prints the result.
Show answer
Initialize a counter to 0, then use nested loops to check every seat: empty_count = 0 for row in seats: for seat in row: if not seat: empty_count += 1 print(f"Empty seats: {empty_count}") The outer loop iterates through each row (a sublist). The inner loop checks each seat in that row. When a seat is False (not taken), the counter increments. After visiting all 3 × 4 = 12 seats, empty_count equals 5 because there are five False values in the grid.

Question 5: CRITICAL THINKING

A student writes the following code intending to print a 5 × 5 multiplication table, but the output is wrong. Identify the bug, explain why it causes incorrect output, and provide the corrected code. for r in range(1, 6): for c in range(1, 6): print(r * r, end="\t") print()
Show answer
The bug is in the expression inside the print statement. The student wrote r r instead of r c. Because the inner loop variable c is never used in the body, every column in a given row prints the same value (the square of r). For example, row 3 would print "9 9 9 9 9" instead of "3 6 9 12 15". The corrected code changes r r to r c: for r in range(1, 6): for c in range(1, 6): print(r * c, end="\t") print() This is a very common nested loop mistake — accidentally using the outer variable where the inner variable belongs. A good debugging habit is to verify that every loop variable is actually used inside its loop body for its intended purpose.

SUMMARY

Lesson Summary

Summary

A nested loop places one loop inside the body of another, allowing you to systematically process two-dimensional structures like grids, tables, and 2D lists. The outer loop controls one dimension (typically rows), while the inner loop controls the other (typically columns). The inner loop runs to completion for every single iteration of the outer loop, producing a total of outer_count × inner_count executions of the innermost body.

You can build text patterns (rectangles, triangles, number grids) by varying the inner loop's range. You can process 2D data by iterating over rows and then over elements within each row. Common pitfalls include mixing up the inner and outer variables, forgetting to reset accumulators between rows, and underestimating how quickly total iterations grow as grid dimensions increase. Mastering nested loops lays the groundwork for list comprehensions, sorting algorithms, and working with multi-dimensional data in future courses.

Varsity Tutors • Computer Programming 1 (Python) • Nested Loops — I can use nested loops at an introductory level to process grids or build simple patterns.