A. Programming Concepts and Skills · Arrays and SubprogramsWriting Your Own Functions
A3.2 A4.2 A4.3About 70 minutes
Learning goals- I can define a function with def, parameters, a body and a return statement, and call it.
- I can explain the difference between a parameter and an argument, and between return and print.
- I can follow the flow of control into a function and back out.
- I can name and document a function using professional conventions (a verb-style snake_case name and a docstring).
Words to know
function definition
The def block that creates a function: its name, parameters and body. It does not run until the function is called.
parameter
A variable named in the function definition that receives a value when the function is called, e.g. temp_c in def wind_chill(temp_c, wind_kmh).
argument
The actual value passed in a call, e.g. -10 in wind_chill(-10, 20).
return statement
return value ends the function and sends the value back to the caller.
procedure
A subprogram that performs an action but does not return a value. In Python it is a function that returns None.
docstring
A string in triple quotes on the first line of a function body that explains what the function does, its parameters and what it returns.
flow of control
The order in which a program's statements run, including jumps into and out of functions.
Write it once, use it everywhere
A weather app for Northern Ontario shows the wind chill on the home screen, in the hourly forecast and in a warning message. The calculation is a long formula. If you copy it into three places and someone finds a mistake, you must fix it three times, and you will probably miss one.
A function solves this. You write the formula once, give it a name, and call it wherever you need it. The curriculum asks you to write your own subprograms, called functions or procedures, that use parameter passing and appropriate scope. This lesson covers writing and calling them; the next two cover parameters and scope in depth.
Remember from Grade 9 math
In Grade 9 you wrote one-line functions such as def cost(h): return 28 + 6 * h and called them with cost(5). Now you will write longer functions, with decisions and loops in their bodies, and document them like a professional.
The parts of a function
def starts the definition.- The name follows the same rules as variable names. By convention it is snake_case and often a verb or a description of the result:
wind_chill, count_below, print_banner. - Parameters go in brackets, separated by commas. They are variables that will receive values when the function is called. A function can have no parameters:
def show_menu():. - A colon ends the header line.
- The body is indented (4 spaces). Its first line should be a docstring in triple quotes saying what the function does.
return value ends the function and sends the value back.
The parts of a function definition. The call area_of_rink(60, 26) passes the arguments 60 and 26 into the parameters length and width; the function returns 1560. (60 m by 26 m is about the size of an NHL ice surface.)Defining is not running
A def block only creates the function. Nothing inside runs until the function is called. When Python reaches a call:
- It evaluates the arguments.
- It jumps to the function and gives each parameter the value of its argument, in order.
- It runs the body until it reaches
return (or the end of the body). - It jumps back to where the call was, and the call is replaced by the returned value.
Because Python reads a file from top to bottom, a function must be defined before the line that calls it runs. Calling it too early gives NameError.
Flow of control. Line 1 only creates the function (its body, lines 2 and 3, does not run yet), so the first real work happens at line 5. The call on line 6 jumps into the function (solid arrow), the parameter n receives 7, and return jumps back (dashed arrow) with the value 14.Worked exampleExample 1: A wind chill function
Environment and Climate Change Canada reports the wind chill index with the formula W = 13.12 + 0.6215T − 11.37V^0.16 + 0.3965T·V^0.16, where T is the air temperature in °C and V is the wind speed in km/h. Write a documented function and use it.
- Choose a clear name and parameter names that include the units:
wind_chill(temp_c, wind_kmh). - Compute V^0.16 once and store it in a local variable v, so the formula is easier to read.
- Return the rounded value instead of printing it, so the caller can print it, compare it or store it.
- The program below calls the function three times with different arguments.
Output: -18, -33 and Frostbite risk is high: -48.
def wind_chill(temp_c, wind_kmh):
"""Return the wind chill index, rounded to a whole number.
Uses the formula from Environment and Climate Change Canada.
temp_c: air temperature in °C; wind_kmh: wind speed in km/h.
"""
v = wind_kmh ** 0.16
w = 13.12 + 0.6215 * temp_c - 11.37 * v + 0.3965 * temp_c * v
return round(w)
print(wind_chill(-10, 20))
print(wind_chill(-20, 30))
feels = wind_chill(-30, 40)
if feels <= -40:
print("Frostbite risk is high:", feels)
The function from Example 1. The docstring explains the purpose, the source of the formula, and the units of each parameter.
return vs. print
This is the most important idea in the lesson. print shows a value on the screen for a person. return hands a value back to the program. A value that is only printed cannot be stored, added or compared.
A function with no return (or a bare return) gives back None. Such a subprogram is sometimes called a procedure: it is used for what it does (for example printing), not for a value.
def print_banner(title):
"""Print title between two lines of = signs."""
print("=" * len(title))
print(title)
print("=" * len(title))
result = print_banner("Snow Report")
print(result)
Output: the banner (===========, Snow Report, ===========) and then None. print_banner is a procedure: it does its job, but there is no value to keep.
Worked exampleExample 2: Several return statements
In Ontario, a percentage mark of 80 to 100 is Level 4, 70 to 79 is Level 3, 60 to 69 is Level 2 and 50 to 59 is Level 1. Write a function that returns the level for a mark.
- A function can have several
return statements. The first one that runs ends the function immediately. - Check from the top down: if mark >= 80, return "Level 4"; otherwise check >= 70, and so on.
- If no test passes, the last line returns "below Level 1". No
else is needed, because every earlier branch already returned. - Code:
def achievement_level(mark):
"""Return the Ontario achievement level for a percentage mark."""
if mark >= 80:
return "Level 4"
elif mark >= 70:
return "Level 3"
elif mark >= 60:
return "Level 2"
elif mark >= 50:
return "Level 1"
return "below Level 1"
for m in [92, 74, 50, 49]:
print(m, achievement_level(m))
92 Level 4, 74 Level 3, 50 Level 1, 49 below Level 1
Functions that work on lists
A parameter can hold a whole list. This turns the algorithms from earlier lessons into reusable tools:
def average(values):
"""Return the mean of a non-empty list of numbers."""
total = 0
for v in values:
total += v
return total / len(values)
marks = [78, 91, 66, 85]
print(average(marks))
print(average([3, 4]) + average([10]))
Output: 80.0, then 13.5 (3.5 + 10.0). Each call runs the loop on a different list.
When a function goes wrong
NameError: name 'wind_chill' is not defined: the call runs before the def, or the name is misspelled.TypeError: wind_chill() missing 1 required positional argument: 'wind_kmh': too few arguments in the call.TypeError: unsupported operand type(s) for +: 'NoneType' and 'int': you used the result of a function that printed instead of returning. See the code below.
def triple(n):
answer = n * 3
print(answer)
total = triple(4) + 1
This prints 12 and then crashes. triple prints its answer but returns None, so None + 1 is a TypeError. Fix: replace print(answer) with return answer.
Just for funA function walks into a café and orders a coffee. The barista hands it the cup. The function drinks it and walks out without paying.
The barista sighs: "Typical. It returned None."
Hands-on activityRecipe functions
You need: a favourite recipe (maple taffy, pancakes, bannock, anything), paper and pencil, and a computer with Python.
- A recipe is like a program. Find a step that repeats or could be reused, such as "convert cups to millilitres" (1 cup is about 250 mL in Canadian recipes) or "scale an amount for more people".
- On paper, design two functions: one that returns a value, e.g.
cups_to_ml(cups), and one procedure that prints a nicely formatted ingredient line, e.g. print_ingredient(name, amount, unit). - Write both with docstrings, then use them to print the whole ingredient list for double the recipe.
- Explain to a family member which function returns and which prints, and why
cups_to_ml must return.
What to look for: names are verbs or results in snake_case, each function has a docstring, and the returning function contains no print.
Your turn
1Write a function square(n) that returns n times n. Call it to print the square of 12.
def square(n):
return n * n
print(square(12))
It prints 144.
2In def area(length, width): and the call area(5, 3), name the parameters and the arguments.
Parameters: length, width. Arguments: 5, 3.
3What does a function return if it has no return statement?
4Predict the output:
def f(x):
return x + 1
print("done")
print(f(f(2)))
4. f(2) = 3, f(3) = 4. "done" is never printed, because return ends the function first.
5Write a function is_freezing(temp_c) that returns True if the temperature is 0 °C or colder.
def is_freezing(temp_c):
return temp_c <= 0
The comparison itself is a Boolean, so it can be returned directly.
6Why is a docstring better than no comment at all?
It tells anyone using the function what it does, what each parameter means and what comes back, without reading the body. help(function_name) also displays it.
7Rename these badly named functions: x(a, b) that returns the larger of two numbers; Stuff(lst) that prints a list one item per line.
For example larger(a, b) and print_each(items): snake_case, describing the result or the action.
More practice: warm-up
1Which keyword starts a function definition?
2What must the body of a function be?
Indented under the header line.
3What symbol ends the header line?
4True or false: a def block runs as soon as Python reads it.
False. It only creates the function; the body runs when the function is called.
Ends the function and sends a value back to the caller.
More practice: on your own
1Write c_to_f(c) that returns the Fahrenheit temperature 9c/5 + 32. What is c_to_f(-40)?
def c_to_f(c):
return 9 * c / 5 + 32
c_to_f(−40) = −72 + 32 = −40.0 (the one temperature where the scales agree).
2Write count_below(values, limit) that returns how many elements of values are less than limit.
def count_below(values, limit):
count = 0
for v in values:
if v < limit:
count += 1
return count
count_below([-14, -3, -22], -10) returns 2.
3Predict the output:
def mystery(a, b):
if a > b:
return a - b
return b - a
print(mystery(3, 10), mystery(10, 3))
7 7. It returns the (positive) difference, like abs(a − b).
4This program crashes with NameError. Why? Fix it.
print(greet("Noor"))
def greet(name):
return "Hi " + name
The call runs before the def has been read, so greet does not exist yet. Move the def above the print. Output: Hi Noor.
5Write a procedure print_receipt(items, prices) that prints each item and price on its own line, then the total. Why is it a procedure?
def print_receipt(items, prices):
"""Print each item with its price, then the total."""
total = 0
for i in range(len(items)):
print(items[i], prices[i])
total += prices[i]
print("Total:", total)
Its job is to display text; it returns nothing (None).
Challenge
1Write is_prime(n) that returns True if the whole number n (n ≥ 2) has no divisors other than 1 and itself. Use it to print the primes below 30.
def is_prime(n):
"""Return True if n (an int >= 2) is prime."""
for d in range(2, n):
if n % d == 0:
return False
return True
primes = []
for k in range(2, 30):
if is_prime(k):
primes.append(k)
print(primes)
Output: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]. Returning False as soon as a divisor is found is an early return.
2Write hypotenuse(a, b) without importing anything, and explain why print(hypotenuse(3, 4)) shows 5.0.
def hypotenuse(a, b):
return (a * a + b * b) ** 0.5
Raising to the power 0.5 is the square root: √(9 + 16) = √25 = 5.0. The result is a float because of the fractional power.
3A function average(values) crashes on an empty list. Rewrite it so it returns None for an empty list, and explain why the caller must still check the result.
def average(values):
"""Return the mean of values, or None if values is empty."""
if len(values) == 0:
return None
total = 0
for v in values:
total += v
return total / len(values)
The caller might do arithmetic with the result, and None + 1 is a TypeError, so it should test if result is not None: first.
Check your understandingQuiz
1. In def bonus(mark, extra):, what are mark and extra?
2. What does this print? def f(n): return n * 2 then print(f(3) + f(4))
3. What is the main difference between return x and print(x) inside a function?
4. A program calls area(5) but the function is def area(length, width):. What happens?
5. Which is the best name and header for a function that returns the number of days below freezing?
6. Where must a function's definition be, compared with the line that first calls it?
For parents and teachersBig idea: Expectation A3.2 asks students to write subprograms (functions and procedures) that use parameter passing and appropriate scope. This lesson covers writing and calling them; the next two lessons go deeper. A4.2 (naming and internal documentation) is practised with docstrings and snake_case names.
How to help: Ask your teen to explain the flow of control diagram in their own words: where does the program start, when does it jump, and what comes back? The recipe activity works well as a conversation in the kitchen.
Common difficulties:
- Printing inside a function instead of returning. If a result needs to be used later, it must be returned.
- Thinking the def block runs on its own. Only a call runs it.
- Mixing up parameters and arguments. Parameters are the names in the definition; arguments are the values in the call.
- Putting code after
return and expecting it to run. - Missing indentation, which causes
IndentationError (a syntax error) before the program even starts.