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
forandrange()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: 5050Notice
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
4Notice
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
7Notice
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
15Notice
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
1Notice
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: 5Notice
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: 55Notice
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: 7Notice
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
FizzBuzzNotice
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
10Notice
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
7Notice
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: TrueNotice
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 5Notice
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 9Notice
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 20Notice
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 25Notice
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
fortakes each number fromrange()in turn.range(n)begins at 0 and stops beforen; a start and step change the visit.- Running totals and conditional counts work inside a
forloop 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.