Parameters and return
Problem
One receipt, different totals
You want a function that prints the last line of the receipt: "Total: €…". But the total is different for every order.
The functions from the last lesson always print the same thing. A function print_total() has no way to know the total. You do not want a separate function for every total either. You need a way to pass the total to the function when you call it.
The idea
Parameters: what the function gets
In the parentheses, write a name for what the function expects. That is a parameter. Inside the function it is an ordinary variable.
When you call it, you put the value in the parentheses. That value is called an argument.
def print_total(total): print(f"Total: €{total}")
print_total(12.5)print_total(4.2)Total: €12.5Total: €4.2total is the parameter. 12.5 and 4.2 are the arguments. There is one function, and it prints different totals.
You do not write a type before the parameter: Python does not check what you pass. If you pass text instead of a number, the function prints it without complaining. That is why the parameter's name has to say clearly what is expected.
If there are several parameters, you separate them with commas. The arguments are passed in the same order.
Try it
By position, not by name
What will the program print?
def print_line(item, qty): print(f"{item}: {qty} pcs")
coffee = "Coffee"qty = 3print_line(coffee, 2)Arguments are matched to parameters by position: the first one to item, the second one to qty. The variable qty outside has nothing to do with the parameter qty, even though they have the same name.
Problem
Why did the price not go down?
You have written a function for a 10% discount and called it:
def apply_discount(price): price = price * 0.9
price = 100apply_discount(price)print(price) # 100100The price is still 100. The function gives no error, and yet the discount is not there. Where did it go?
See it
The function gets the value
def apply_discount(price): price = price * 0.9
price = 100apply_discount(price)print(price) # 100Step by step, in words
- In the program there is a variable price = 100.
- You call apply_discount(price): new memory is set aside for the function.
- Its parameter price gets the value 100.
- The function works out 100 * 0.9 = 90 and stores the new number in its own price.
- The function ends and its memory goes away, along with its price.
- And price in the program is still 100.
When you call it, the function does not get the variable price itself, but its value, in its own variable price. The line price = price * 0.9 puts the new number 90 into the function's variable. When the function ends, its memory goes away, and that 90 goes with it. Nothing has changed outside: it is as if the function got a copy. This is called pass by value.
The two variables have the same name (price) on purpose: that is exactly what confuses people in practice. The name does not matter. Every function has its own variables.
More precisely, if you are curious
Strictly speaking, Python does not copy the number. The parameter points to the same number 100 that price outside points to. But a number cannot be changed in place: price = … only points the function's name at another number, and price outside still points to 100. That is why for numbers and text the result is the same as with a copy. Lists are different: the function can change the list itself (with append, for example), and the change is visible outside too:
def add_item(items): items.append("milk")
cart = ["bread"]add_item(cart)print(cart)['bread', 'milk']The idea
return: the function returns a result
The fix is for the function to return the new price. In the body you write return and the value. You do not write a type for the result: def is the same whether or not the function returns something.
def apply_discount(price): return price * 0.9
price = 100price = apply_discount(price)print(price) # 90.0Step by step, in words
- In the program: price = 100.
- You call apply_discount(price): memory is set aside for the function.
- The parameter price gets the value 100.
- return price * 0.9: the result is 90.
- The result goes back to the program and is stored in price.
- Now price in the program is 90.
The call apply_discount(price) is now a value. You can store it in a variable (as here), print it or calculate with it.
More precisely, if you are curious
- A function without
returnstill returns something:None, the "nothing" value. If you print the result of the first version, which only lowered its own copy, that is exactly what you see:
def apply_discount(price): price = price * 0.9
print(apply_discount(100))None- You can also write
returnwith no value. It just ends the function.
- A function can return several values at once: you list them with commas after
return, and in the call you store them in as many variables:
def get_limits(): return 1, 10
low, high = get_limits()print(low, high)1 10Try it
What is the result?
The function works out a 20% VAT on the price. What should go in the blank so that whoever calls it gets the result?
def vat(price):
price * 0.2return: without it the function works out price * 0.2 and throws the result away, and the call gives None.Try it
Returned, but lost
What will the program print?
def reserve(seats): seats = seats - 2 print(f"inside: {seats}") return seats
seats = 10reserve(seats)print(f"outside: {seats}")The function changes its own variable and returns 8, but nobody stores the result. To keep it, you need seats = reserve(seats).
The idea
return ends the function
The shop does not charge for delivery on orders of €50 or more. Otherwise delivery is €4.99:
def shipping_cost(total): if total >= 50: return 0 return 4.99
print(shipping_cost(62.0))print(shipping_cost(18.5))04.99As soon as it reaches return, the function ends right away. That is why at €62 the second return does not run, and we do not need an else.
A function with a result has to return a value on every path. But Python does not check this. If you remove the last line, the program runs, and for an order of €18.50 the function reaches its end without a return and returns None:
def shipping_cost(total): if total >= 50: return 0
print(shipping_cost(62.0))print(shipping_cost(18.5))0NoneThere is no error, so a slip like this is hard to catch. Watch the output and try every path through the function.
Try it
Find the bug
Club members get a 10% discount, and everyone else pays the full price. But everyone gets the discount. Which line is the bug on?
Pick the line with the bug.
The last return is for people who are not members: they pay the full price, return price.
Exercise
Functions that calculate
Solve the tasks in your editor. Each function gets what it needs as parameters and returns the result. The printing happens outside the functions, in the lines below them.
1. Average. The function average(values) returns the average of the numbers in the list. Below it, read from the keyboard how many numbers there are, then the numbers themselves.
51015202530Average: 20.02. Prime number. The function is_prime(n) returns whether the number is prime (divisible only by 1 and by itself). For 7 it returns True, for 12 False.
3. Temperatures. The functions to_fahrenheit(c) and to_celsius(f). F = C · 9 / 5 + 32. Read a temperature and a unit (C or F) and print the converted one.
100C100.0 C = 212.0 F4. Calculator. The function calculate(a, b, op) returns the result for +, -, * or /. The operation is chosen with if.
105+10.0 + 5.0 = 15.05. A triangle with a parameter. We promised it in the last lesson. The function print_triangle(height) prints a right triangle of asterisks with height height. For 3:
** ** * *More precisely, if you are curious
In Python, / always gives a decimal number, so 5 / 9 is 0.5555…, not 0 as in Java and C#. Integer division is //:
print(5 / 9)print(7 // 2)0.55555555555555563Exercise
Functions for lists
Each task is one function that gets a list (or a word) and returns a result. At the end, put them all together in one program.
total,sum_evenandsum_odd: the sum of all the numbers, of the even ones and of the odd ones in the list.reverse: returns a new list with the numbers in reverse order.largestandsmallest: the largest and the smallest number.is_palindrome(word): whether the word reads the same forward and backward.sum_diagonalsandsum_diagonals_once: the sum along both diagonals of a square matrix, with the center element counted twice and once.- All together: for the list
[1, 2, 3, 4, 5], the number 7 (withis_primefrom the card above), the words "level" and "ABCDE" and the 3 × 3 matrix with the numbers from 1 to 9.
In Python, sum, max and min are already built-in functions. If you name your own function sum, it hides the built-in one in the whole program. That is why ours are called total, largest and smallest.
Sum: 15, Even: 6, Odd: 9Reversed: [5, 4, 3, 2, 1]Max: 5, Min: 17 is prime: Truelevel: True, ABCDE: FalseSum with repeats: 30, Sum without repeats: 25Test