Working with functions#
In this exercise, we will look at defining your own functions in Python. Functions provide a neat way of reusing code, resulting in shorter and clearer programs. This notebook covers the input to functions, output from functions and how they are used. There are exercises throughout the notebook, and the solutions are available at the bottom.
Defining and calling a function#
Here a simple python function is defined. The def keyword is used to start a definition of a function and is followed by the name of the function. The input for the function is within parenthesis, but our first print_hello function has no input. After the input, a colon is used to signify the start of the function body. The function body is executed whenever the function is called, and needs to be indented. The function body ends at the first line that is not indented.
def print_hello():
print("Hello World")
print_hello() # Calling our defined function
Hello World
Function input#
Functions can take arguments, which makes them more flexible. Consider the following.
def print_twice(x):
print(
2 * x
) # When an integer is multiplied by a string, it is printed that number of times
print_twice("bye ")
bye bye
It is allowed to have several arguments, making the function yet more flexible. Note, however, that the number of arguments used when calling the function must match the function definition.
def print_n(x, n):
print(n * x)
print_n("-><-", 10)
print_n("-||-", 10)
-><--><--><--><--><--><--><--><--><--><-
-||--||--||--||--||--||--||--||--||--||-
(1) Task: Before running the function below, what would you expect to happen when swapping the input arguments?
Show code cell content
print_n(10, "-><-")
print_n(10, "-||-")
-><--><--><--><--><--><--><--><--><--><-
-||--||--||--||--||--||--||--||--||--||-
Keyword arguments#
When a function takes several arguments, it can be difficult to remember the order and purpose of each. Keyword arguments have a default value, and can be called with their name. They can also be called in any order, and even neglected as a default value is available.
def print_kw(string="", repeat=1):
print(repeat * string)
print_kw(string="/<>", repeat=10)
print_kw(repeat=10, string="/<>")
print_kw(string="______________________________")
/<>/<>/<>/<>/<>/<>/<>/<>/<>/<>
/<>/<>/<>/<>/<>/<>/<>/<>/<>/<>
______________________________
Function output#
Functions can also return output directly to the caller using the return keyword.
def add_five(x):
return x + 5
y = add_five(4)
print(y)
9
It is even possible to have multiple outputs from a function, they are returned as a tuple, but can be extracted as they are returned. If the output of a function with multiple return values is assigned to a single variable, the entire tuple is stored there.
def add_and_subtract_five(x):
return x + 5, x - 5
a, b = add_and_subtract_five(11) # The returned tuple is extracted into a and b
print(a)
print(b)
16
6
c = add_and_subtract_five(11) # two outputs assigned to a single variable
print(c) # c now contains a tuple
print(c[0], c[1]) # tuples are accessed like lists
(16, 6)
16 6
Exercises#
Plenty of exercises can be done with the ingredients available now!
(2) Task: Write a function that multiply two inputs and returns the result, but if the result is larger than 100, it should be halved.
Solution:
Show code cell content
def multiply_and_maybe_halve(x, y):
out = x * y
return out / 2 if out > 100.0 else out
a = multiply_and_maybe_halve(5, 8)
print(a)
b = multiply_and_maybe_halve(12, 9)
print(b)
40
54.0
(3) Task:
Write a function that combines two lists
Solution:
Show code cell content
def combine_lists(first_list, second_list):
return first_list + second_list
combine_lists([1, 2, 3], ["a", "b", "c"])
[1, 2, 3, 'a', 'b', 'c']
(4) Task: Combine the following three functions into one, choosing between the styles using a keyword argument.
def print_among_stars(x):
print(10 * "*" + x + 10 * "*")
def print_among_lines(x):
print(10 * "-" + x + 10 * "-")
def return_among_lines(x):
return 10 * "-" + x + 10 * "-"
Solution:
Show code cell content
def print_among(x, style="stars"):
chars = {"stars": "*", "lines": "-", "return": "-"}
pad = chars[style] * 10
out = pad + x + pad
if style == "return":
return out
print(out)
print_among("hurray")
print_among("hurray", style="lines")
a = print_among("hurray", style="return")
a
**********hurray**********
----------hurray----------
'----------hurray----------'
Variable number of input parameters#
In most cases where a function has to work on some scalable input, it is easiest to pass a list to the function, as in this example.
def sum_list(x):
output = 0
for element in x:
output += element
return output
s = sum_list([1, 2, 10])
print(s)
13
Python can, however, make the input of a function into a list automatically by defining the input parameter with a *.
def sum_input(*args):
output = 0
for element in args:
output += element
return output
s = sum_input(1, 2, 10, 100)
print(s)
113
The same principle can be applied for keyword arguments by using two stars, and these are then assembled into a dictionary. The name kwargs is often used, denoting “keyword arguments”. The next example of a function just assembles the given keyword variables into a dict, but prints “quack” in case a variable named “duck” is among them.
def make_dict(**kwargs):
if "duck" in kwargs:
print("quack")
return kwargs
d = make_dict(h=3, speed=2000, string="hello")
print(d)
{'h': 3, 'speed': 2000, 'string': 'hello'}
e = make_dict(duck=3)
print(e)
quack
{'duck': 3}
Doc strings#
It is good practice to write comments in code, and functions are no exception. Python functions support what is called doc strings which is placed at the start of the function, and can then later be shown with the help function.
def make_dict(**kwargs):
"""
Returns given keyword arguments as dictionary
Prints quack in case a duck was contained in the keyword arguments
"""
if "duck" in kwargs:
print("quack")
return kwargs
d = make_dict(h=3, speed=2000, string="hello")
print(d)
{'h': 3, 'speed': 2000, 'string': 'hello'}
help(make_dict)
Help on function make_dict in module __main__:
make_dict(**kwargs)
Returns given keyword arguments as dictionary
Prints quack in case a duck was contained in the keyword arguments
Escalating scope#
If a variable is used within a Python function that is not defined as input or within the function, the python interpreter will look for it outside the function. This can produce surprising results and is not necessarily considered a good practice.
def check_below_3():
if outside_par < 3: # outside_par never defined!
print("outside_par is below 3!")
else:
print("outside_par is 3 or above!")
outside_par = 5 # outside_par defined before running the function
check_below_3()
outside_par = 2 # change in outside_par reflected in next function call
check_below_3()
outside_par is 3 or above!
outside_par is below 3!
Storing function definitions in variables#
It is possible to store a reference to a function as a variable, or pass the name of the function to another function. This can also aid in adding flexibility to functions.
def add(x, y):
out = []
for i in range(len(x)):
out.append(x[i] + y[i])
return out
def apply_operation(x, y, op):
"""
Apply an operation between two input arrays and return the result.
"""
return op(x, y)
x = [1, 2, 3, 4, 5]
y = [6, 7, 8, 9, 10]
apply_operation(x, y, add)
[7, 9, 11, 13, 15]
(5) Task:
Define a new function to multiply the array elements, and use it with apply_operation
.
Solution:
Show code cell content
def multiply(x, y):
out = []
for i in range(len(x)):
out.append(x[i] * y[i])
return out
apply_operation(x, y, multiply)
[6, 14, 24, 36, 50]
Recursive functions#
Functions can also call themselves to perform recursive operations.
(6) Task: Write a function that will recursively decrement the input in steps of 1, until the result is 0.
Solution:
Show code cell content
def decrement(x):
print(x)
x = x - 1
if x < 1:
return x
return decrement(x)
decrement(10)
10
9
8
7
6
5
4
3
2
1
0