We have seen that Python has several built-in functions (e.g. print()
or max()
). But you can also create your own function. A function is a reusable block of code that performs a specific task. Once you have defined a function, you can use it at any place in your Python script. You can even import a function from an external module (as we will see in the next chapter). Therefore, they are very useful for tasks that you will perform more often. Plus, functions are a convenient way to order your code and make it more readable!
We use an example from this website to show you some of the basics of writing a function. We use some materials from thisother Python course.
Now let's get started!
If you have questions about this chapter, please refer to the forum on Canvas.
A function is an isolated chunk of code, that has a name, gets zero or more parameters, and returns a value. In general, a function will do something for you, based on a number of input parameters you pass it, and it will typically return a result. You are not limited to using functions available in the standard library or the ones provided by external parties. You can also write your own functions!
Whenever you are writing a function, you need to think of the following things:
There are several good reasons why functions are a key component of any non-ridiculous programmer:
Let's say we want to sing a birthday song to Emily. Then we print the following lines:
In [ ]:
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear Emily.")
print("Happy Birthday to you!")
This could be the purpose of a function: to print the lines of a birthday song for Emily. Now, we define a function to do this. Here is how you define a function:
def
;Statements must be indented, so that Python knows what belongs in the function and what not. Functions are only executed when you call them. It is good practice to define your functions at the top of your program.
We give the function a clear name, happy_birthday_to_emily
and we define the function as shown below. Note that we specify exactly what it does in the docstring in the beginning of the function:
In [ ]:
def happy_birthday_to_emily(): # Function definition
"""
Print a birthday song to Emily.
"""
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear Emily.")
print("Happy Birthday to you!")
If we execute the code above, we don't get any output. That's because we only told Python: "Here's a function to do this, please remember it." If we actually want Python to execute everything inside this function, we have to call it:
It is important to distinguish between a function definition and a function call. We illustrate this in 1.3.1. You can also call functions from within other functions. This will become useful when you split up your code into small chunks that can be combined to solve a larger problem. This is illustrated in 1.3.2.
A function is defined once. After the definition, Python has remembered what this function does in its memory. A function is executed/called as many times as we like. When calling a function, you should always use parenthesis.
In [ ]:
# function definition:
def happy_birthday_to_emily(): # Function definition
"""
Print a birthday song to Emily.
"""
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear Emily.")
print("Happy Birthday to you!")
# function call:
print('Function call 1')
happy_birthday_to_emily()
print()
# We can call the function as many times as we want (but we define it only once)
print('Function call 2')
happy_birthday_to_emily()
print()
print('Function call 3')
happy_birthday_to_emily()
print()
# This will not call the function
#(without the parenthesis, Python thinks that `happy_birthday_to_emily` is a variable and not a function!):
print('This is not a function call')
happy_birthday_to_emily
In [ ]:
def new_line():
"""Print a new line."""
print()
def two_new_lines():
"""Print two new lines."""
new_line()
new_line()
print("Printing a single line...")
new_line()
print("Printing two lines...")
two_new_lines()
print("Printed two lines")
You can do the same tricks that we learnt to apply on the built-in functions, like asking for help
or for a function type
:
In [ ]:
help(happy_birthday_to_emily)
In [ ]:
type(happy_birthday_to_emily)
The help we get on a function will become more interesting once we learn about function inputs and outputs ;-)
We use parameters and arguments to make a function execute a task depending on the input we provide. For instance, we can change the function above so we can input the name of a person and print a birthday song using this name. This results in a more generic function.
To understand how we use parameters and arguments, keep in mind the distinction between function definition and function call.
Parameter: The variable name
in the function definition below is a parameter. Variables used in function definitions are called parameters.
Argument: The variable my_name
in the function call below a value for the parameter name
at the time when the function is called. We denote such variables arguments. We use arguments so we can direct the function to do different kinds of work when we call it at different times.
In [ ]:
# function definition with using the parameter `name'
def happy_birthday(name):
"""
Print a birthday song with the "name" of the person inserted.
"""
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear " + name + ".")
print("Happy Birthday to you!")
In [ ]:
# function call using specifying the value of the argument
happy_birthday("James")
We can also store the name in a variable:
In [ ]:
my_name="James"
happy_birthday(my_name)
If we forgt to specify the name, we get an error:
In [ ]:
happy_birthday()
Functions can have multiple parameters. We can for example multiply two numbers in a function (using the two parameters x and y) and then call the function by giving it two arguments:
In [ ]:
def multiply(x, y):
"""Multiply two numeric values."""
result = x * y
print(result)
multiply(2020,5278238)
multiply(2,3)
The function definition tells Python which parameters are positional and which are keyword. As you might remember, positional means that you have to give an argument for that parameter; keyword means that you can give an argument value, but this is not necessary because there is a default value.
So, to summarize these two notes, we distinguish between the following four categories:
1) positional parameters: (we indicate these when defining a function, and they are compulsory when calling the function)
2) keyword parameters: (we indicate these when defining a function, but they have a default value - and are optional when calling the function)
3) positional arguments: (we MUST specify these when calling a function)
4) keyword arguments: (we CAN specify these when calling a function)
To summarize:
parameters | arguments | |
---|---|---|
positional | indicated in definition | compulsory in call |
keyword | indicated in definition | optional in call |
For example, if we want to have a function that can either multiply two or three numbers, we can make the third parameter a keyword parameter with a default of 1 (remember that any number multiplied with 1 results in that number):
In [ ]:
def multiply(x, y, third_number=1): # x and y are positional parameters, third_number is a keyword parameter
"""Multiply two or three numbers and print the result."""
result=x*y*third_number
print(result)
In [ ]:
multiply(2,3) # We only specify the positional arguments
multiply(2,3,third_number=4) # We specify both the positional arguments, and the keyword argument
If we do not specify a positional argument, the function call will fail (with a very helpful error message):
In [ ]:
multiply(3)
Functions can have a return statement. The return
statement returns a value back to the caller and always ends the execution of the function. This also allows us to use the result of a function outside of that function by assigning it to a variable:
In [ ]:
def multiply(x, y):
"""Multiply two numbers and return the result."""
result = x * y
return result
#here we assign the returned value to variable z
result = multiply(2, 5)
print(result)
We can also print the result directly (without assigning it to a varible), which gives us the same effect as using the print statements we used before:
In [ ]:
print(multiply(30,20))
If we assign the result to a variable, but do not use the return statement, the function cannot return it. Instead, it returns None
(as you can try out below).
This is important to realize: even functions without a return
statement do return a value, albeit a rather boring one. This value is called None
(it’s a built-in name). You have seen this already with list methods - for example list.append(val)
adds a value to a list, but does not return anything explicitly.
In [ ]:
def multiply_no_return(x, y):
"""Multiply two numbers and does not return the result."""
result = x * y
is_this_a_result = multiply_no_return(2,3)
print(is_this_a_result)
Returning multiple values
Similarly as the input, a function can also return multiple values as output. We call such a collection of values a tuple (does this term sound familiar ;-)?).
In [ ]:
def calculate(x,y):
"""Calculate product and sum of two numbers."""
product = x * y
summed = x + y
#we return a tuple of values
return product, summed
# the function returned a tuple and we unpack it to var1 and var2
var1, var2 = calculate(10,5)
print("product:",var1,"sum:",var2)
Make sure you actually save your 2 values into 2 variables, or else you end up with errors or unexpected behavior:
In [ ]:
#this will assign `var` to a tuple:
var = calculate(10,5)
print(var)
#this will generate an error
var1,var2,var3 = calculate(10,5)
Saving the resulting values in different variables can be useful when you want to use them in different places in your code:
In [ ]:
def sum_and_diff_len_strings(string1, string2):
"""
Return the sum of and difference between the lengths of two strings.
"""
sum_strings = len(string1) + len(string2)
diff_strings = len(string1) - len(string2)
return sum_strings, diff_strings
sum_strings, diff_strings = sum_and_diff_len_strings("horse", "dog")
print("Sum:", sum_strings)
print("Difference:", diff_strings)
Docstring is a string that occurs as the first statement in a function definition.
For consistency, always use """triple double quotes""" around docstrings. Triple quotes are used even though the string fits on one line. This makes it easy to later expand it.
There's no blank line either before or after the docstring.
The docstring is a phrase ending in a period. It prescribes the function or method's effect as a command ("Do this", "Return that"), not as a description; e.g. don't write "Returns the pathname ...".
In practice, there are several formats for writing docstrings, and all of them contain more information than the single sentence description we mention here. Probably the most well-known format is reStructured Text. Here is an example of a function description in reStructured Text (reST):
In [ ]:
def my_function(param1, param2):
"""
This is a reST style.
:param param1: this is a first param
:param param2: this is a second param
:returns: this is a description of what is returned
:raises keyError: raises an exception
"""
return
You can see that this docstring describes the function goal, its parameters, its outputs, and the errors it raises.
It is a good practice to write a docstring for your functions, so we will always do this! For now we will stick with single-sentence docstrings
So far we have seen functions that follow a single thread of code (always return the same thing). We can also condition the return
value of the function by using if-else
statements. For instance, let's have a function that prints "even" or "odd" depending on the input number. If the number is zero, then the function prints "zero".
In [ ]:
def even_or_odd(p):
"""Check whether a number is even or odd."""
if p==0:
return "zero"
if p%2 ==1: # odd number
return "odd"
else: # even number
return "even"
num = int(input("Please enter a number> "))
print(even_or_odd(num))
In [ ]:
def is_even(p):
"""Check whether a number is even."""
if p%2 ==1:
return False
else:
return True
num = int(input("Please enter a number> "))
if is_even(num):
print(num, "is even")
else:
print(num, "is odd")
Any variables you declare in a function, as well as the parameters that are passed to a function will only exist within the scope of that function, i.e. inside the function itself. The following code will produce an error, because the variable x
does not exist outside of the function:
In [ ]:
def setx():
"""Set the value of a variable to 1."""
x = 1
setx()
print(x)
Even when we return x, it does not exist outside of the function:
In [ ]:
def setx():
"""Set the value of a variable to 1."""
x = 1
return x
setx()
print(x)
Also consider this:
In [ ]:
x = 0
def setx():
"""Set the value of a variable to 1."""
x = 1
setx()
print(x)
In fact, this code has produced two completely unrelated x
's!
So, you can not read a local variable outside of the local context. Nevertheless, it is possible to read a global variable from within a function, in a strictly read-only fashion. But as soon as you assign something, the variable will be a local copy:
In [ ]:
x = 1
def getx():
"""Print the value of a variable x."""
print(x)
getx()
You can use two built-in functions in Python when you are unsure whether a variable is local or global. The function locals()
returns a list of all local variables, and the function globals()
- a list of all global variables. Note that there are many non-interesting system variables that these functions return, so in practice it is best to check for membership with the in
operator. For example:
In [ ]:
a=3
b=2
def setb():
"""Set the value of a variable b to 11."""
b=11
c=20
print("Is 'a' defined locally in the function:", 'a' in locals())
print("Is 'b' defined locally in the function:", 'b' in locals())
setb()
print("Is 'a' defined globally:", 'a' in globals())
print("Is 'b' defined globally:", 'b' in globals())
print("Is 'c' defined globally:", 'c' in globals())
Finally, note that the local context stays local to the function, and is not shared even with other functions called within a function, for example:
In [ ]:
def setb_again():
"""Set the value of a variable to 3."""
b=3
print("in 'setb_again' b =", b)
def setb():
"""Set the value of a variable b to 2."""
b=2
setb_again()
print("in 'setb' b =", b)
b=1
setb()
print("global b =", b)
We call the function setb()
from the global context, and we call the function setb_again()
from the context of the function setb()
. The variable b
in the function setb_again()
is set to 3, but this does not affect the value of this variable in the function setb()
which is still 2. And as we saw before, the changes in setb()
do not influence the value of the global variable (b=1
).
Exercise 1:
Write a function that converts meters to centimeters and prints the resulting value.
In [ ]:
# you code here
Exercise 2:
Add another keyword parameter message
to the multiply function, which will allow a user to print a message. The default value of this keyword parameter should be an empty string. Test this with 2 messages of your choice. Also test it without specifying a value for the keyword argument when calling a function.
In [ ]:
# function to modify:
def multiply(x, y, third_number=1):
"""Multiply two or three numbers and print the result."""
result=x*y*third_number
print(result)
Exercise 3:
Write a function called multiple_new_lines
which takes as argument an integer and prints that many newlines by calling the function newLine.
In [ ]:
def new_line():
"""Print a new line."""
print()
# you code here
Exercise 4:
Let's refactor the happy birthday function to have no repetition. Note that previously we print "Happy birthday to you!" three times. Make another function happy_birthday_to_you()
that only prints this line and call it inside the function happy_birthday(name)
.
In [ ]:
def happy_birthday_to_you():
# your code here
# original function - replace the print statements by the happy_birthday_to_you() function:
def happy_birthday(name):
"""
Print a birthday song with the "name" of the person inserted.
"""
print("Happy Birthday to you!")
print("Happy Birthday to you!")
print("Happy Birthday, dear " + name + ".")
print("Happy Birthday to you!")
Exercise 5:
Try to figure out what is going on in the following examples. How does Python deal with the order of calling functions?
In [ ]:
def multiply(x, y, third_number=1):
"""Multiply two or three numbers and print the result."""
result=x*y*third_number
return result
print(multiply(1+1,6-2))
print(multiply(multiply(4,2),multiply(2,5)))
print(len(str(multiply(10,100))))
Exercise 6:
Complete this code to switch the values of two variables:
In [ ]:
def switch_two_values(x,y):
# your code here
a='orange'
b='apple'
a,b = switch_two_values(a,b) # `a` should contain "apple" after this call, and `b` should contain "orange"
print(a,b)
In [ ]: