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?

Hide 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:

Hide 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:

Hide 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:

Hide 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:

Hide 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:

Hide 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