Python, Concept by Concept

unit

For & Range

A game club needs a 5-by-5 multiplication board. Each row must be assembled as one string and printed on its own line:

1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25

How can a program visit every row and every column without a separate instruction for each product?

We will build that payoff in three lessons. First, for visits a known sequence of integers. Then familiar total-and-count patterns use that sequence. Finally, one loop inside another builds tables and triangles.

You will learn

  • Use for and range() to visit a known sequence of numbers.
  • Choose a start, stop, and step, then use each number in a calculation.
  • Put one loop inside another to build rows and columns.

Lesson 1: Visit a Range

Reproduce the 5050 sum

Unit 02 showed this total as a boxed peek. Unit 04 rebuilt it with while. Here is the same running total with for and range.

total = 0

for n in range(1, 101):
    total = total + n

print(f"Sum: {total}")

Output

Sum: 5050

Notice

This reproduces Sum: 5050. range(1, 101) supplies the integers 1 through 100, and n receives one value per pass. Do not modify the for variable inside its loop body.

Two forms of range

range(n) starts at 0. It supplies 0 through n - 1.

for number in range(5):
    print(number)

Output

0
1
2
3
4

Notice

The output is 0, 1, 2, 3, 4. The stop value 5 is not included.

range(a, b) starts at a and stops before b.

for number in range(3, 8):
    print(number)

Output

3
4
5
6
7

Notice

range(3, 8) includes 3 but not 8, so the final printed value is 7. Set the stop one beyond the last value you want.

One twist: a third range argument chooses the step size.

for number in range(0, 20, 5):
    print(number)

Output

0
5
10
15

Notice

This prints 0, 5, 10, 15. The stop value 20 is excluded even when the step is 5.

One twist: a negative step counts down, so the stop must be below the start.

for number in range(10, 0, -1):
    print(number)

Output

10
9
8
7
6
5
4
3
2
1

Notice

The countdown prints 10 through 1; it stops before 0.

Real input: choose the upper bound for a count from 1.

Try it yourself

n = int(input("How high? "))
for number in range(1, n + 1):
    print(number)

Notice

int() converts the typed number; the range includes it by stopping at n + 1.

Comment the job, not every pass

A short comment can explain why a loop exists.

# Print the five badge numbers.
for badge_number in range(1, 6):
    print(f"Badge: {badge_number}")

Output

Badge: 1
Badge: 2
Badge: 3
Badge: 4
Badge: 5

Notice

The name badge_number explains the changing value, and the comment explains the loop's purpose.

Lesson 2: Calculate across a Range

Running totals and conditional counts

The accumulator patterns from Unit 04 work unchanged. Only the source of each number changes.

square_total = 0

for number in range(1, 6):
    square_total = square_total + number * number

print(f"Sum of squares: {square_total}")

Output

Sum of squares: 55

Notice

The range supplies 1 through 5, and the accumulator produces Sum of squares: 55.

count = 0

for number in range(1, 31):
    if number % 4 == 0:
        count = count + 1

print(f"Count: {count}")

Output

Count: 7

Notice

Every integer from 1 through 30 is checked, but only seven values pass the divisibility test.

FizzBuzz with an elif ladder

FizzBuzz classifies each integer. Multiples of both 3 and 5 must be checked first because they also match the separate tests.

for number in range(1, 16):
    if number % 15 == 0:
        line = "FizzBuzz"
    elif number % 3 == 0:
        line = "Fizz"
    elif number % 5 == 0:
        line = "Buzz"
    else:
        line = f"{number}"
    print(line)

Output

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

Notice

Each pass assigns exactly one string to line. Use a print(line) at the loop-body level (same indent as the if), after the ladder. The order lets 15 become FizzBuzz.

One twist: continue skips the rest of one for pass and moves to the next value.

for n in range(1, 11):
    if n % 3 == 0:
        continue
    print(n)

Output

1
2
4
5
7
8
10

Notice

3, 6, and 9 are skipped; every other number from 1 through 10 prints.

Real input: choose the last FizzBuzz number. The familiar decision ladder now runs over the typed range.

Try it yourself

n = int(input("How high? "))
for number in range(1, n + 1):
    if number % 15 == 0:
        line = "FizzBuzz"
    elif number % 3 == 0:
        line = "Fizz"
    elif number % 5 == 0:
        line = "Buzz"
    else:
        line = f"{number}"
    print(line)

Notice

The input changes only the range endpoint; each number still gets one label.

Find a divisor, then test for prime

A divisor divides a number with no remainder. First, stop as soon as one divisor of 91 is found.

Minimal: print the first divisor of 91, then leave the search loop.

for d in range(2, 91):
    if 91 % d == 0:
        print(d)
        break

Output

7

Notice

7 is the first divisor found; break ends this for loop immediately.

One twist: keep a boolean flag so the search also reports whether a number is prime.

n = 29
is_prime = True

for divisor in range(2, n):
    if n % divisor == 0:
        is_prime = False
        break

print(f"{n} is prime: {is_prime}")

Output

29 is prime: True

Notice

break stops this loop when a divisor proves the flag false. For n == 2, range(2, 2) is empty, so is_prime stays True.

Lesson 3: Put One Loop inside Another

Build rows in stages

First, build one row string by adding one star per pass.

row_text = ""
for col in range(1, 5):
    row_text = row_text + "*"
print(row_text)

Output

****

Notice

One loop builds **** as a single string; printing happens after the row is complete.

One twist: repeat that row-building loop inside an outer loop to make a rectangle.

for row in range(1, 4):
    row_text = ""
    for col in range(1, 5):
        row_text = row_text + "*"
    print(row_text)

Output

****
****
****

Notice

The inner body has 8 spaces and adds four stars. print(row_text) has 4 spaces, so it runs once per completed row. A break in the inner loop would leave only that loop.

A growing triangle

The row number can control how many columns the inner loop visits.

height = 5

for row in range(1, height + 1):
    row_text = ""
    for col in range(1, row + 1):
        row_text = row_text + "*"
    print(row_text)

Output

*
**
***
****
*****

Notice

Row 1 gets one inner pass, while row 5 gets five. The first accumulated row is *; the last is *****.

One twist: string repetition can make each triangle row without an inner loop.

height = 5
for r in range(1, height + 1):
    print("*" * r)

Output

*
**
***
****
*****

Notice

"*" * r makes exactly as many stars as the row number. The earlier loop shows how to build the same row one star at a time.

One twist: print(piece, end="") keeps the next piece on the same line; a plain print() finishes it.

for col in range(1, 5):
    print("*", end="")
print()

Output

****

Notice

The four pieces print as **** on one line. The final print() moves to the next line.

One number row

Before building a full table, make a single row with numbers separated by one space.

row_text = ""
for col in range(1, 6):
    if col == 1:
        row_text = f"{col}"
    else:
        row_text = row_text + f" {col}"
print(row_text)

Output

1 2 3 4 5

Notice

The result is 1 2 3 4 5 with no trailing space; the first number needs no separator.

A multiplication table

Build each table row as a string. A small condition inserts spaces between products without leaving a hidden trailing space.

table_size = 3

for row in range(1, table_size + 1):
    row_text = ""
    for col in range(1, table_size + 1):
        product = row * col
        if col == 1:
            row_text = f"{product}"
        else:
            row_text = row_text + f" {product}"
    print(row_text)

Output

1 2 3
2 4 6
3 6 9

Notice

The first row is 1 2 3 and the last is 3 6 9. The inner loop builds one complete row_text; the outer loop prints three rows. No special print options are needed.

One twist: a width inside an f-string, f"{number:3}", pads the number with spaces in front until it is 3 characters wide.

row_text = ""
for col in range(1, 6):
    row_text = row_text + f"{col * 4:3}"
print(row_text)

Output

  4  8 12 16 20

Notice

The row prints as 4 8 12 16 20. Every product fills exactly 3 characters — one-digit numbers get two spaces in front, two-digit numbers get one — so the columns of a table line up. The padding is on the left, so no row ends with a trailing space.

ASCII pattern ladder

Start with the triangle you built above. These next shapes change one row rule at a time.

One twist: visit row lengths in reverse to make an inverted triangle.

height = 4
for r in range(height, 0, -1):
    print("*" * r)

Output

****
***
**
*

Notice

The negative step makes rows of 4, 3, 2, then 1 star.

One twist: add leading spaces so the triangle lines up on the right.

height = 4
for r in range(1, height + 1):
    print(" " * (height - r) + "*" * r)

Output

   *
  **
 ***
****

Notice

The spaces shrink as the stars grow. No row ends with trailing spaces.

One twist: widen each star row to odd widths to center a pyramid.

height = 4
for r in range(1, height + 1):
    print(" " * (height - r) + "*" * (2 * r - 1))

Output

   *
  ***
 *****
*******

Notice

The star widths are 1, 3, 5, and 7, so the rows form a centered pyramid.

Real input: choose the height of the pyramid.

Try it yourself

height = int(input("Height: "))
for r in range(1, height + 1):
    print(" " * (height - r) + "*" * (2 * r - 1))

Notice

The typed height controls both the leading spaces and each row's star width.

Final build

Return to the 5-by-5 multiplication board. The outer loop selects rows 1 through 5, the inner loop selects columns 1 through 5, and string accumulation makes one assertable row at a time.

table_size = 5

for row in range(1, table_size + 1):
    row_text = ""
    for col in range(1, table_size + 1):
        product = row * col
        if col == 1:
            row_text = f"{product}"
        else:
            row_text = row_text + f" {product}"
    print(row_text)

Output

1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25

Notice

The finished board matches the opening exactly. Known ranges choose the positions, nested loops visit every row-column pair, and each row is printed only after its string is complete.

Recap

  • for takes each number from range() in turn.
  • range(n) begins at 0 and stops before n; a start and step change the visit.
  • Running totals and conditional counts work inside a for loop too.
  • In nested loops, the inner loop finishes for each outer-loop pass.

You can now build a multiplication board one row at a time.

Go to the exercises (31)

For parents and teachers: report a problem