Learning Goals
3 minBy the end of this lesson you can:
- Write a function from scratch given only its name, parameters and expected behaviour.
- Choose
printvsreturncorrectly for each event without being told which to use. - Test your own functions in the main program by calling them and comparing the output to the expected output.
The Rules
5 minSet a timer for 50 minutes — the last 10 minutes are for the recap and the homework. Open one file, decathlon.py. Write all ten functions in it. Three rules to play by:
- Read each brief carefully. The brief tells you the function's name, its parameters, what it should return (or print), and a worked example. Match the names exactly — the tests at the bottom call them by name.
- No built-in shortcuts unless the brief says so. If an event says "write your own version of
max", you don't usemax. If it doesn't mention a built-in, use whatever you like. - Test as you go. Don't write all ten then run. Write event 1, scroll to the bottom, call it once with the example input, compare to the expected output, then move to event 2.
Skip and come back. Three half-done events are worse than seven complete ones. The final two are deliberately the hardest — don't let them eat the clock.
The Decathlon · Ten Events
35 minEvent 1 · greet(name)
Prints Hello, <name>! on one line. No return needed.
greet("Aisyah") # → Hello, Aisyah! greet("Faiz") # → Hello, Faiz!
Event 2 · square(n)
Returns n * n. No print inside.
print(square(6)) # → 36 print(square(11)) # → 121
Event 3 · is_odd(n)
Returns True if n is odd, else False. Use %.
print(is_odd(3)) # → True print(is_odd(10)) # → False print(is_odd(0)) # → False
Event 4 · add_three(a, b, c)
Returns the sum of three numbers. Three parameters.
print(add_three(1, 2, 3)) # → 6 print(add_three(10, 20, 30)) # → 60
Event 5 · max_of_two(a, b)
Returns the larger of two numbers. Use if/else — don't use the built-in max.
print(max_of_two(7, 4)) # → 7 print(max_of_two(2, 9)) # → 9 print(max_of_two(5, 5)) # → 5
Event 6 · is_vowel(letter)
Returns True if letter (a single character) is one of a, e, i, o, u — in any case. Else False. Hint: .lower() first.
print(is_vowel("a")) # → True print(is_vowel("E")) # → True print(is_vowel("z")) # → False
Event 7 · countdown(n)
Prints n, then n-1, all the way down to 1, then prints Go!. Use a while loop (from PY-L1-12) or a for with range.
countdown(3) # → # 3 # 2 # 1 # Go!
Event 8 · total(nums)
Returns the sum of all numbers in the list. Don't use the built-in sum. Loop and accumulate.
print(total([1, 2, 3, 4])) # → 10 print(total([10, 20, 30])) # → 60 print(total([])) # → 0
Event 9 · shout(text)
Returns text in upper case with !!! stuck on the end. Use .upper() (from PY-L1-23) and string +.
print(shout("hello")) # → HELLO!!! print(shout("ayam goreng")) # → AYAM GORENG!!!
Event 10 · fizz_buzz_one(n)
The classic interview question, one number at a time. Return "FizzBuzz" if n is divisible by both 3 and 5; "Fizz" if just 3; "Buzz" if just 5; otherwise return str(n) — yes, cast the number to a string so all four return values are the same type.
print(fizz_buzz_one(3)) # → Fizz print(fizz_buzz_one(5)) # → Buzz print(fizz_buzz_one(15)) # → FizzBuzz print(fizz_buzz_one(7)) # → 7 (as a string "7") print(fizz_buzz_one(30)) # → FizzBuzz
Gotcha for event 10. Check the "both" case first. If you check n % 3 == 0 first, the 15 case will return "Fizz" instead of "FizzBuzz" and never reach the both-check.
You should be on Event 6 or 7 by now. If you're still on Event 3, slow down and read the briefs more carefully — most of them are 2-3 lines of body.
Quick Demo · How to Structure Your File
5 minDon't reinvent the file structure. Use this skeleton:
# decathlon.py — Functions Decathlon # ---- Event 1 ---- def greet(name): # your code here pass # placeholder so the file runs while empty # ---- Event 2 ---- def square(n): pass # ... events 3..10 ... # ===== TESTS ===== print("Event 1:") greet("Aisyah") greet("Faiz") print("Event 2:") print(square(6)) print(square(11)) # ... add tests as you finish each event ...
The pass keyword is a polite Python placeholder — it lets a function body be empty without crashing the file. Replace it with real code as you finish each event.
Write your tests in the same order as your events, and add them as you go. When all ten tests print the right answers, you've won.
Self-Check
3 minWhen you think you're done, ask yourself:
- Did every event that says returns actually use
return, notprint? - Did every event that says prints actually use
print, notreturn? - For booleans (Events 3 and 6) — did you return
True/Falsedirectly, not the strings"True"/"False"? - For Event 10 — did you check the
FizzBuzzcase before theFizz-only case?
7-8 events done → solid. 9-10 events with all tests passing → you've nailed functions. Don't fudge it — re-run the failing tests and fix the bugs, you'll learn more from one fix than from skipping ahead.
Stretch · Eleventh Event
8 minFinished the ten with time to spare? Try this one. Don't peek at the hint.
Event 11 · fizz_buzz_list(n)
Returns a list with the FizzBuzz result for every number from 1 up to and including n. Use your fizz_buzz_one from Event 10 as a helper — composition!
print(fizz_buzz_list(5)) # → ['1', '2', 'Fizz', '4', 'Buzz'] print(fizz_buzz_list(15)) # → ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', # '11', 'Fizz', '13', '14', 'FizzBuzz']
Show one possible solution
def fizz_buzz_list(n): answer = [] for i in range(1, n + 1): answer.append(fizz_buzz_one(i)) return answer
Four lines because Event 10 did the work. range(1, n + 1) goes from 1 up to and including n (the + 1 is the trick from PY-L1-13). The list is built with the accumulator pattern from PY-L1-31. The whole event is a victory lap for composition — your old function powering your new one.
Recap
3 minTen tiny functions, one file, sixty minutes. The decathlon was a deliberate sweep through everything PY-L1-26 to PY-L1-33 taught you — no parameters, one parameter, multiple parameters, defaults, boolean returns, list parameters, string methods, loops inside functions, returning vs printing. If you got all ten tests passing, you have everything you need for Hangman and the capstone. If a few got away from you, write down which ones and re-try them tonight — they'll all show up again. Tomorrow we start a three-part Hangman build.
Vocabulary Card
pass- A do-nothing placeholder. Lets an empty function body or loop body parse without errors.
str(n)- Built-in that converts a value to a string. Useful for returning numbers as strings when the caller expects a string.
- test as you go
- The habit of running and checking each function the moment you finish it, before moving to the next.
- function composition
- Calling one of your own functions from inside another —
fizz_buzz_listusesfizz_buzz_one. - order-sensitive
if - An
if/elifchain where the first matching branch wins. Check the most-specific case first (theFizzBuzztrap).
Homework
4 minTake home your decathlon.py. Two jobs:
- If any event didn't finish in class, finish it tonight. Add a one-line comment above the function —
# finished at home— so you know in a week which ones you cracked under pressure and which needed extra time. - Add one more test per event so each function gets called at least twice with different inputs. Real programmers don't trust a function until they've seen it work on more than one case.
Bring decathlon.py next class. In PY-L1-35 we start Hangman — and you'll use today's habits to write five small functions before lunchtime.
Sample · decathlon.py
# decathlon.py — Functions Decathlon (sample answers) def greet(name): print("Hello,", name + "!") def square(n): return n * n def is_odd(n): return n % 2 != 0 def add_three(a, b, c): return a + b + c def max_of_two(a, b): if a > b: return a else: return b def is_vowel(letter): return letter.lower() in ["a", "e", "i", "o", "u"] def countdown(n): while n >= 1: print(n) n = n - 1 print("Go!") def total(nums): answer = 0 for x in nums: answer = answer + x return answer def shout(text): return text.upper() + "!!!" def fizz_buzz_one(n): if n % 15 == 0: return "FizzBuzz" elif n % 3 == 0: return "Fizz" elif n % 5 == 0: return "Buzz" else: return str(n) # ===== TESTS ===== print("Event 1:") greet("Aisyah"); greet("Faiz") print("Event 2:", square(6), square(11)) print("Event 3:", is_odd(3), is_odd(10), is_odd(0)) print("Event 4:", add_three(1, 2, 3), add_three(10, 20, 30)) print("Event 5:", max_of_two(7, 4), max_of_two(2, 9), max_of_two(5, 5)) print("Event 6:", is_vowel("a"), is_vowel("E"), is_vowel("z")) print("Event 7:") countdown(3) print("Event 8:", total([1, 2, 3, 4]), total([10, 20, 30]), total([])) print("Event 9:", shout("hello"), shout("ayam goreng")) print("Event 10:", fizz_buzz_one(3), fizz_buzz_one(5), fizz_buzz_one(15), fizz_buzz_one(7), fizz_buzz_one(30))
A couple of slick moves in the sample worth noticing. is_vowel uses letter.lower() in [...] — the in operator works on lists. fizz_buzz_one checks n % 15 == 0 instead of n % 3 == 0 and n % 5 == 0 (a number divisible by 3 AND 5 is divisible by 15, same thing, less typing). And shout uses method chaining: call .upper() on text and add "!!!" in one expression. Tiny moves, big style points.