Warm-up · Activity 1 of 7
// B2.5 · ~40 min · Beginner
Build: FizzBuzz variants and a menu with match
After this build you can combine every tool of module B2 in one program: FizzBuzz and its variants, and a text menu that loops with while True and picks commands with match.
Lesson 5 of 5 in B2 Control flow
You will be able to
- Write FizzBuzz and its variants with for, range, if and match
- Drive a text menu with while True, match, break and continue
- Spot the classic slips in such programs: condition order, off-by-one ranges and text in guards
Predict · Activity 2 of 7
A FizzBuzz variant. Predict before you read on: what does this print?
for i in range(1, 7): if i % 3 == 0: print("Fizz", end=" ") elif i % 2 == 0: print("Even", end=" ") else: print(i, end=" ")Practice · Activity 3 of 7
Fill in the pattern that matches any remainder, without binding a name, so that 9 prints Fizz.
for i in [15, 9, 10, 7]: match [i % 3, i % 5]: case [0, 0]: print("FizzBuzz") case [0, ____]: print("Fizz") case [_, 0]: print("Buzz") case _: print(i)case [0, ]:Practice · Activity 4 of 7
A menu loop over a list of commands. What does this print?
log = [] for command in ["help", "", "add", "quit", "add"]: match command: case "": continue case "quit": break case "help" | "?": log.append("help") case _: log.append(command) print(log)Practice · Activity 5 of 7
Put the steps of one round of a menu loop in order.
- 1.Run the command, or break in the quit case
- 2.Read a line and split it into words
- 3.Match the list of words against the command patterns
- 4.Start the round: while True:
Brain teaser · Activity 6 of 7
Brain teaser. After the loop, what does the last line print?
for i in range(1, 16): match [i % 3, i % 5]: case [0, _]: word = "Fizz" case [_, 0]: word = "Buzz" case [0, 0]: word = "FizzBuzz" case _: word = "" print(word)Apply · Activity 7 of 7
Mini-task. Extend FizzBuzz with a third rule: multiples of 7 add Bazz. The words combine, so 21 prints FizzBazz and 105 prints FizzBuzzBazz. Read n with int(input()) and print one line per number from 1 to n. Hint: build the word with three separate ifs.
Check your work against this list
Build it yourself
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
A FizzBuzz lab with a menu
This program puts the whole module together. A while True menu reads commands: help, fizzbuzz N prints FizzBuzz up to N using match, first N searches for the first FizzBuzz with break and a loop else, and quit ends the program. Empty lines are skipped, and a guard with isdigit() turns away arguments that are not numbers. The input below feeds it seven lines; change them and run it again.
main.py
# A small FizzBuzz lab, driven by a menu. Each input line is one command.
while True:
words = input().split()
match words:
case []:
continue # an empty line: ask again
case ["quit" | "q"]:
print("Bye!")
break
case ["help"]:
print("Commands: fizzbuzz N, first N, help, quit")
case ["fizzbuzz", n] if n.isdigit():
for i in range(1, int(n) + 1):
match [i % 3, i % 5]:
case [0, 0]:
print("FizzBuzz", end=" ")
case [0, _]:
print("Fizz", end=" ")
case [_, 0]:
print("Buzz", end=" ")
case _:
print(i, end=" ")
print()
case ["first", n] if n.isdigit():
# The first number up to n that is a FizzBuzz, if there is one.
for i in range(1, int(n) + 1):
if i % 15 == 0:
print("First FizzBuzz:", i)
break
else:
print("No FizzBuzz up to", n)
case [command, *_]:
print("Unknown command:", command)
input.txt
help
fizzbuzz 15
first 10
first 40
fizzbuzz ten
quit
Run it with
python main.py < input.txtOutput
Commands: fizzbuzz N, first N, help, quit
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
No FizzBuzz up to 10
First FizzBuzz: 15
Unknown command: fizzbuzz
Bye!- The empty third line gives [], so case [] continues without printing anything.
- first 10 finds no multiple of 15, so the loop ends without break and its else runs.
- fizzbuzz ten fails the isdigit() guard, so match falls through to case [command, *_].
- The print() after the inner loop ends the line that the end=" " prints left open.
Change it and run it
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Exercises
Exercise 1 of 3
Step 1: classic FizzBuzz
The program reads a whole number n. Print the numbers from 1 to n, one per line, but print Fizz for multiples of 3, Buzz for multiples of 5 and FizzBuzz for multiples of both. Use if and elif, or match on [i % 3, i % 5]. For 0, print nothing.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
Inside the loop, decide what to print for i with an if, elif and else chain.
Hint 2
Test for both first: i % 15 == 0 (or i % 3 == 0 and i % 5 == 0), or 15 stops at Fizz.
Hint 3
With match: match [i % 3, i % 5]: then case [0, 0]:, case [0, _]:, case [_, 0]: and case _:.
Show a solution
One way to solve it. Yours can look different and still pass the checks.
n = int(input())
for i in range(1, n + 1):
if i % 15 == 0:
print("FizzBuzz")
elif i % 3 == 0:
print("Fizz")
elif i % 5 == 0:
print("Buzz")
else:
print(i)
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
n = int(input())
for i in range(1, n + 1):
# Replace multiples of 3 with Fizz, of 5 with Buzz, of both with FizzBuzz.
print(i)
test_main.py
from learnrun import run_main
def fizzbuzz(n):
return run_main(f"{n}\n").split()
def test_first_five():
"""1 to 5: 1 2 Fizz 4 Buzz"""
got = fizzbuzz(5)
assert got == ["1", "2", "Fizz", "4", "Buzz"], f"for 5 the program printed {got!r}"
def test_fifteen():
"""15 is FizzBuzz, not Fizz"""
got = fizzbuzz(15)
assert len(got) == 15, f"for 15 the program printed {len(got)} lines, expected 15"
assert got[-1] == "FizzBuzz", f"the 15th line is {got[-1]!r}, expected 'FizzBuzz' (check the order of your tests)"
def test_to_thirty():
"""Lines 25 to 30 are Buzz 26 Fizz 28 29 FizzBuzz"""
got = fizzbuzz(30)[24:]
assert got == ["Buzz", "26", "Fizz", "28", "29", "FizzBuzz"], f"lines 25 to 30 are {got!r}"
def test_zero():
"""0 prints nothing"""
got = fizzbuzz(0)
assert got == [], f"for 0 the program printed {got!r}, expected nothing"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyExercise 2 of 3
Step 2: stop at the first FizzBuzz
Change FizzBuzz for n so that it stops right after printing the first FizzBuzz: nothing after it is printed. If the loop reaches n without any FizzBuzz, print no FizzBuzz as a last line. Use match on [i % 3, i % 5], break, and else on the for loop.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
break inside a case ends the for loop around the match.
Hint 2
Put break right after print("FizzBuzz"), in the same case.
Hint 3
Write else: print("no FizzBuzz") at the indentation of for: it runs only when no break happened.
Show a solution
One way to solve it. Yours can look different and still pass the checks.
n = int(input())
for i in range(1, n + 1):
match [i % 3, i % 5]:
case [0, 0]:
print("FizzBuzz")
break
case [0, _]:
print("Fizz")
case [_, 0]:
print("Buzz")
case _:
print(i)
else:
print("no FizzBuzz")
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
n = int(input())
for i in range(1, n + 1):
match [i % 3, i % 5]:
case [0, 0]:
print("FizzBuzz")
case [0, _]:
print("Fizz")
case [_, 0]:
print("Buzz")
case _:
print(i)
# Stop after the first FizzBuzz; otherwise print "no FizzBuzz".
test_main.py
from learnrun import run_main
def lines(n):
return run_main(f"{n}\n").splitlines()
def test_stops_at_fifteen():
"""For 20 it stops after line 15, FizzBuzz"""
got = lines(20)
assert len(got) == 15, f"for 20 the program printed {len(got)} lines, expected 15"
assert got[-1] == "FizzBuzz", f"the last line is {got[-1]!r}, expected 'FizzBuzz'"
def test_no_fizzbuzz():
"""For 7 all seven lines appear, then no FizzBuzz"""
got = lines(7)
assert got == ["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "no FizzBuzz"], f"for 7 the program printed {got!r}"
def test_exactly_fifteen():
"""For 15 the loop breaks, so no FizzBuzz is not printed"""
got = lines(15)
assert "no FizzBuzz" not in got, "for 15 the program printed 'no FizzBuzz', but it did find one"
assert got[-1] == "FizzBuzz", f"the last line is {got[-1]!r}, expected 'FizzBuzz'"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyExercise 3 of 3
Step 3: a calculator menu
Build a menu that keeps a total, starting at 0. Read lines until quit. add N and sub N change the total when N is a whole number (check with N.isdigit() in a guard). show prints the total. quit prints bye and ends. Skip empty lines, and print unknown command for anything else.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
Each round: words = input().split(), then match words: with one case per command.
Hint 2
case ["add", n] if n.isdigit(): runs only when n is a number; "add x" then falls through to case _.
Hint 3
case []: continue skips empty lines; case ["quit"]: prints bye and breaks.
Show a solution
One way to solve it. Yours can look different and still pass the checks.
total = 0
while True:
words = input().split()
match words:
case []:
continue
case ["add", n] if n.isdigit():
total = total + int(n)
case ["sub", n] if n.isdigit():
total = total - int(n)
case ["show"]:
print(total)
case ["quit"]:
print("bye")
break
case _:
print("unknown command")
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
total = 0
while True:
words = input().split()
# Match words: add N, sub N, show, quit, an empty line, anything else.
break
test_main.py
from learnrun import run_main
def menu(*commands):
return run_main("\n".join(commands) + "\n").splitlines()
def test_add_and_show():
"""add 5, add 3, show prints 8"""
got = menu("add 5", "add 3", "show", "quit")
assert got == ["8", "bye"], f"the program printed {got!r}, expected ['8', 'bye']"
def test_sub():
"""add 10, sub 4, show prints 6"""
got = menu("add 10", "sub 4", "show", "quit")
assert got == ["6", "bye"], f"the program printed {got!r}, expected ['6', 'bye']"
def test_empty_lines():
"""Empty lines are skipped; the total starts at 0"""
got = menu("", "show", "", "quit")
assert got == ["0", "bye"], f"the program printed {got!r}, expected ['0', 'bye']"
def test_unknown():
"""Unknown commands and non-numbers are refused"""
got = menu("jump", "add x", "add", "quit")
assert got == ["unknown command"] * 3 + ["bye"], f"the program printed {got!r}"
def test_quit_stops():
"""Nothing after quit is read"""
got = menu("quit", "add 1", "show")
assert got == ["bye"], f"the program printed {got!r}, expected only ['bye']"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyCommon mistakes
Adding a number to text
i = 3
print("Fizz" + i)
What Python prints
TypeError: can only concatenate str (not "int") to strWhy, and the fix
+ joins two strings or adds two numbers, but not one of each. Print them side by side, print("Fizz", i), or turn the number into text first with str(i).
Converting the whole line
line = "add 5" # what input() gives you
n = int(line)
What Python prints
ValueError: invalid literal for int() with base 10: 'add 5'Why, and the fix
int() needs text that is only a number. Split the line first and convert just the argument: words = line.split(), then int(words[1]), or capture it with case ["add", n]: and call int(n).
Comparing captured text with a number
match "add 5".split():
case ["add", n] if n > 0:
print("adding", n)
What Python prints
TypeError: '>' not supported between instances of 'str' and 'int'Why, and the fix
Patterns capture parts of a split line as strings, so n is "5", not 5. Convert in the guard, if int(n) > 0, and when the text might not be a number, check n.isdigit() first: if n.isdigit() and int(n) > 0.
Python in the browser: Pyodide 314.0.7, MPL-2.0. Licence and source
Exit ticket
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.