In the exercise at the end of the last chapter, you edited a script that could encode messages to Morse code. The script is good, but is not very easy to use or reusable. For someone to make use of the script, they will have to edit it and copy and paste your code every time they want to encode a different message.
Functions provide a way of packaging code into reusable and easy-to-use components. We saw plenty of examples of functions in the last chapter, e.g. print()
wraps up all the logic about exactly how to print things, all you need to do is pass in some arguments and it handles the rest. Likewise with math.sqrt()
, you don't need to understand the algorithm it uses, simply what it needs you to pass it, and what it returns back to you.
You can also bundle up your own logic into functions, allowing you to avoid repeating yourself and make your code easier to read. To explain how they work, lets imagine we are writing some code to help us with baking recipes. Often you will need to convert between different units, for example from ounces to grams. Open a new text file, name it convert.py
and type the following:
weight_in_ounces = 6
weight_in_grams = weight_in_ounces * 28.3495
print(f"{weight_in_grams} g")
python convert.py
You can see this script as having three main parts to it:
weight_in_ounces
The data processing section will work regardless of what data is inside the variable weight_in_ounces
and so we can grab that bit of code and make it usable in other contexts quite easily, using functions.
We can turn this into a function that can add any two arrays together by using def
. To do this, type:
def ounces_to_grams(weight):
new_weight = weight * 28.3495
return new_weight
This has created a new function called ounces_to_grams
which we can now call. In a similar fashion to other constructs in Python (like for
loops and if
statements) it has a rigid structure.
First we must use the def
keyword to start a function definition:
↓ def ounces_to_grams(weight): new_weight = weight * 28.3495 return new_weight
Then we specify the name that we want to give the function. Like anything in Python, choose a descriptive name that describes what it does. This is the name which we will use when calling the function:
↓ def ounces_to_grams(weight): new_weight = weight * 28.3495 return new_weight
Function definitions must then be followed by a pair of round brackets. This is a similar syntax to that used when calling a function and giving it arguments but here we're just defining it:
↓ ↓ def ounces_to_grams(weight): new_weight = weight * 28.3495 return new_weight
Between those brackets go the names of the parameters we want the function to accept. We can define zero or more parameters. Here we are defining one:
↓ def ounces_to_grams(weight): new_weight = weight * 28.3495 return new_weight
Finally, the line is completed with a colon:
↓ def ounces_to_grams(weight): new_weight = weight * 28.3495 return new_weight
Since we've used a colon, we must indent the body of the function as we did with loops and conditional statements:
def ounces_to_grams(weight): new_weight = weight * 28.3495 ← body of return new_weight ← function
Most functions will also want to return data back to the code that called it. You can choose what data is returned using the return
keyword followed by the data you want to return:
def ounces_to_grams(weight): new_weight = weight * 28.3495 return new_weight ↑
The body of the function has been copied from our script above with the only change being that the variables have different names.
You can now call the function using:
def ounces_to_grams(weight):
new_weight = weight * 28.3495
return new_weight
weight_in_ounces = 6
weight_in_grams = ounces_to_grams(weight_in_ounces)
print(f"{weight_in_grams} g")
python convert.py
In this case you have called the function ounces_to_grams
and passed in the argument weight_in_ounces
.
In the fuction, weight_in_ounces
is copied to its internal variable, weight
. The function ounces_to_grams
then acts on weight
, creating the new varaible new_weight
.
It then returns new_weight
, which is assigned to weight_in_grams
.
You can use your new ounces_to_grams
function to convert any numbers. Try typing:
def ounces_to_grams(weight):
new_weight = weight * 28.3495
return new_weight
weight_in_ounces = 999
weight_in_grams = ounces_to_grams(weight_in_ounces)
print(f"{weight_in_grams} g")
python convert.py
Note that we can pass the values to the function directly, e.g. type:
def ounces_to_grams(weight):
new_weight = weight * 28.3495
return new_weight
weight_in_grams = ounces_to_grams(12)
print(f"{weight_in_grams} g")
python convert.py
Take the following code:
my_list = [5, 7, 34, 5, 3, 545]
big_numbers = []
for num in my_list:
if num > 10:
big_numbers.append(num)
print(big_numbers)
and convert the data-processing parts to a function called big
which can be called like:
my_list = [5, 7, 34, 5, 3, 545]
large_numbers = big(my_list)
print(large_numbers)
giving
[34, 545]
Be careful to pay attention to the indentation, ensuring that it is consistent with the original code. Particularly, note that the return
statement will cause the function to exit, so make sure that it doesn't run until after the loop has finished.
Note that you must pass in the right number of arguments to a function. ounces_to_grams
expects one arguments, so if you pass more or less, then that is an error. Try this now:
def ounces_to_grams(weight):
new_weight = weight * 28.3495
return new_weight
weight_in_grams = ounces_to_grams() # We've removed the arguments to this function
print(f"{weight_in_grams} g")
python convert.py
so as you can see, it tells you that you've given it the wrong number of arguments. It expects 1 (weight
). Likewise, if you give too many arguments you get a similar error:
def ounces_to_grams(weight):
new_weight = weight * 28.3495
return new_weight
weight_in_grams = ounces_to_grams(12, 10) # We've passed too many arguments now
print(f"{weight_in_grams} g")
python convert.py
It is possible to define functions that take no arguments:
def pi():
return 3.14159
answer = pi()
single arguments:
def double(x):
return x * 2
answer = double(4)
or lots of arguments:
def lots_of_args(a, b, c, d, e):
return {"a": a, "b": b, "c": c, "d": d, "e": e}
answer = lots_of_args(1, 2, 3, 4, 5)
Take encode.py
from the previous chapter and edit it so that the part that does the conversion (everything from morse = []
to " ".join(morse)
) is moved into a function called encode
. The function should take one argument and return the encoded morse string.
To be more explicit, replace the following lines of code:
morse = []
for letter in message:
letter = letter.lower()
morse_letter = letter_to_morse[letter]
morse.append(morse_letter)
morse_message = " ".join(morse)
and replace them with:
def encode(message):
...
return ...
morse_message = encode(message)
where the ...
should be replaced with the code to do the conversion and the variable to be returned.