Learning Goals
3 minBy the end of this lesson you can:
- Use a
forloop withrange(n)to repeat a block exactlyntimes. - Use
range(start, stop)to count from any starting number to any ending number. - Use the third argument of
rangeto step by twos, fives, or even backwards.
Warm-Up
5 minLast lesson's times_table.py needed five lines just to control the loop — start the counter, check the condition, do the work, update the counter, hope you didn't forget any of those four steps. Today we'll see Python's tidier shape that bundles them into one line.
Quick predict-the-output puzzle. What does this print?
for i in range(4): print("hello", i)
Show the answer
hello 0 hello 1 hello 2 hello 3
Notice two surprises: it counted from 0, and it stopped before 4. That's range in a nutshell — and we'll explore why in a moment.
for handles the counter automatically. range is the helper that says "give me these numbers, in order". Together they replace nearly every counter-based while you'd ever write.
New Concept · for + range()
12 minPart 1 · The shape
for VARIABLE in range(...): do something with VARIABLE
Read it aloud: "for each value in this range, do this block." Every pass through the loop, Python automatically updates VARIABLE to the next value — you do not write VARIABLE = VARIABLE + 1 yourself. That whole class of "I forgot to increment" infinite-loop bugs simply disappears.
Part 2 · range(n) — start at 0, stop before n
range(n) hands out the numbers 0, 1, 2, …, n − 1 — that's exactly n numbers in total, but it does not include n itself.
for i in range(5): print(i)
Python's "half-open" rule keeps two things tidy: the count matches the argument (range(5) produces 5 numbers), and range(0, n) + range(n, m) joins up perfectly with no gap and no overlap. You'll get used to it fast.
Part 3 · range(start, stop) — count from any number
Two arguments: start at the first, stop before the second.
for n in range(1, 6): print(n)
Mantra: "start is included, stop is not." So range(1, 6) gives you 1 through 5 — exactly what you want when a human says "from 1 to 5".
Part 4 · range(start, stop, step) — skip-counting
The third argument is the step: how big a jump between numbers. Defaults to 1; can be any whole number, including negative.
for n in range(2, 11, 2): print(n) for n in range(10, 0, -1): print(n)
The negative-step version is gold for countdowns. The countdown to launch from PY-L1-12's predict-the-output puzzle becomes one short loop.
Part 5 · for vs while — which one?
Reach for a for loop when you already know how many times to repeat — "print 12 rows", "iterate 1 to 10", "do this for every discount level". Times tables, fixed score boards, pattern printers.
Reach for a while loop when the number of repeats depends on something inside the loop — user input, a condition that flips during the run. "Keep asking until they type quit", "play rounds until someone reaches three wins".
Both loops are valid for either job, but the right choice makes the code obviously correct. Whenever a counter is doing all the work, reach for for.
Worked Example · Times Table, Reborn
12 minPhase 1 · The old while version
Here's the times-table loop from last lesson's homework:
while version (6 control lines)
base = int(input("Which number's times table? ")) i = 1 while i <= 12: print(i, "×", base, "=", i * base) i = i + 1
It works perfectly — but four of those six lines are paperwork: start i, check i, update i, hope you didn't typo.
Phase 2 · The for version
for version (3 lines, same job)
base = int(input("Which number's times table? ")) for i in range(1, 13): print(i, "×", base, "=", i * base)
range(1, 13) gives 1, 2, …, 12 — exactly the rows we want. No i = 1, no i = i + 1, no chance of forgetting either. Save this as times_table_for.py and run it for a few different numbers.
Output (base = 7)
Which number's times table? <strong>7</strong> 1 × 7 = 7 2 × 7 = 14 3 × 7 = 21 4 × 7 = 28 5 × 7 = 35 6 × 7 = 42 7 × 7 = 49 8 × 7 = 56 9 × 7 = 63 10 × 7 = 70 11 × 7 = 77 12 × 7 = 84
Phase 3 · A brand-new use — Discount Schedule
Cikgu Faridah's bookshop wants a sign showing the price of a RM 40 book at every discount level from 5% to 50%, in steps of 5%. Save this as discount_schedule.py.
Code
# discount_schedule.py — print prices at 5%, 10%, ..., 50% off list_price = 40.00 print("Discount schedule for a RM", list_price, "book:") for pct in range(5, 55, 5): saved = list_price * pct / 100 pay = list_price - saved print(pct, "% off → save RM", saved, "· pay RM", pay)
Output
Discount schedule for a RM 40.0 book: 5 % off → save RM 2.0 · pay RM 38.0 10 % off → save RM 4.0 · pay RM 36.0 15 % off → save RM 6.0 · pay RM 34.0 20 % off → save RM 8.0 · pay RM 32.0 25 % off → save RM 10.0 · pay RM 30.0 30 % off → save RM 12.0 · pay RM 28.0 35 % off → save RM 14.0 · pay RM 26.0 40 % off → save RM 16.0 · pay RM 24.0 45 % off → save RM 18.0 · pay RM 22.0 50 % off → save RM 20.0 · pay RM 20.0
Reading the loop
range(5, 55, 5)hands out5, 10, 15, 20, 25, 30, 35, 40, 45, 50. The stop value55is not included — that's how we land on exactly 50.- The variable name
pctis just our choice — Python doesn't care, but a good name explains the role. - Inside the loop,
pctchanges automatically each pass. We never have to writepct += 5.
Try It Yourself
13 minRun each task with at least two different inputs to see the pattern.
Ask the user for a number n. Use a for loop with range(n) to print "Hello!" that many times.
Hint
n = int(input("How many hellos? ")) for i in range(n): print("Hello!")
You don't have to use i inside the loop — it's just along for the ride. Some coders use _ instead of i to mean "I don't need this value".
Print every even number from 2 to 20, one per line, using a for loop with a step of 2.
Hint
for n in range(2, 21, 2): print(n)
Remember the half-open rule: to include 20, your stop value must be at least 21.
Print 10, 9, 8, …, 1 one per line, then a final line "🚀 Blast off!". Use one for loop with a negative step.
Hint
for n in range(10, 0, -1): print(n) print("🚀 Blast off!")
The stop value is 0 because we want to print 1 but not 0 — and range stops before the stop value.
Mini-Challenge · FizzBuzz Lite
8 minFizzBuzz is the most famous coding warm-up in the world. Today's lite version is the perfect for-loop drill.
Your file must:
- Use a
forloop to walk through the numbers 1 to 20 (inclusive). - For each number, print one of four things:
- If the number is divisible by both 3 and 5 → print
"FizzBuzz". - Else if it's divisible by 3 → print
"Fizz". - Else if it's divisible by 5 → print
"Buzz". - Otherwise → print the number itself.
- If the number is divisible by both 3 and 5 → print
Remember: n % 3 == 0 is true when n divides exactly by 3 (the remainder operator from PY-L1-04).
Stretch goal. Walk all the way to 100 instead of 20, and keep a running count of how many Fizzes, Buzzes, and FizzBuzzes happened. Print the three counters at the end.
Show one possible solution
# fizzbuzz_lite.py — the classic with a Malaysian twist fizz_count = 0 buzz_count = 0 fizzbuzz_count = 0 for n in range(1, 21): by_three = n % 3 == 0 by_five = n % 5 == 0 if by_three and by_five: print("FizzBuzz") fizzbuzz_count = fizzbuzz_count + 1 elif by_three: print("Fizz") fizz_count = fizz_count + 1 elif by_five: print("Buzz") buzz_count = buzz_count + 1 else: print(n) print() print("Fizz count: ", fizz_count) print("Buzz count: ", buzz_count) print("FizzBuzz count:", fizzbuzz_count)
Branch order matters. Checking by_three and by_five first stops the simpler rules from grabbing a 15 before the combined rule sees it. If you flip the order, every multiple of 15 would print "Fizz" instead of "FizzBuzz".
Recap
3 minfor + range together replace nearly every counter-based while. The half-open rule (start included, stop excluded) is the one thing that surprises everyone once. Master it, and you can step forwards, backwards, or by any size — no manual counter, no off-by-one.
Vocabulary Card
- for loop
- A loop that walks through a fixed sequence of values, updating its variable automatically each pass.
- range(n)
- Hands out the numbers
0, 1, …, n − 1. That's exactlynnumbers;nitself is not included. - range(start, stop)
- Counts from
startup to but not includingstop. - range(start, stop, step)
- Same, jumping by
stepeach time. Negativestepcounts backwards. - iteration
- One single pass through a loop's body.
Homework
4 minCreate a new file called savings_plan.py — a tiny weekly savings projector for someone saving up for a new pair of football boots.
Rules:
- Ask the user for their
name, how much they can save each week (weekly, usefloat()), and the number ofweeksto project (useint()). - Use a
forloop andrange(1, weeks + 1)to print one line per week. Each line should read like:"Week 1: RM 5.0 (running total RM 5.0)". - Keep a running total inside the loop — Python won't compute it for you.
- After the loop, print a friendly summary:
"{name}, in {weeks} weeks you'll have RM {total}." - Stretch. Add a second
forloop that prints a countdown of the same numbers fromweeksdown to1using a negative-step range — labelled "Reverse view".
Bring savings_plan.py next class — we'll combine loops with patterns and pictures in PY-L1-14.
Sample · savings_plan.py
# savings_plan.py — weekly savings projector name = input("Your name? ") weekly = float(input("Save how much per week (RM)? ")) weeks = int(input("Project for how many weeks? ")) total = 0.0 for week in range(1, weeks + 1): total = total + weekly print("Week", week, "· this week: RM", weekly, "· running total: RM", total) print() print(name + ", in", weeks, "weeks you'll have RM", total) # Stretch — reverse view print() print("Reverse view") for week in range(weeks, 0, -1): print("Week", week)
Your wording can vary. The non-negotiables are: a for week in range(1, weeks + 1): loop, a total that grows inside the loop (not before it), and the stretch loop using a negative step like range(weeks, 0, -1). Don't worry about pretty spacing — that's a Level 2 topic (f-strings).