4.1. Functions#

4.1.1. Introduction#

In this section, you will learn all about functions. You have already been exposed to several functions in this textbook, such as the print() function. My goal in this section is to teach you how to create your own functions.

Functions exist in all programming languages. They allow you to write a set of code that can be used later in your program. They are essential because they allow you to not rewrite the same block of code over and over again. Good code is code that does not repeat. In other words, if your program has a task that it needs to do repetitively, then you should develop that code into a clean function (or class which we will meet in the next section).

A function, as we will see has four parts:

  1. the name of the function

  2. the arguments passed to the function (optional)

  3. a docstring that explains the function (optional)

  4. the code of the function

  5. the returned data (optional)

By the end of this section, you will understand each of these components.

4.1.2. Functions in Action#

I think the best way to learn about functions is to see them in action before breaking down what is happening. Let’s take a look at the code below.

def adding_one(number):
    x = number+1
    return x

Let’s break down the code above by starting with line one.

def adding_one(number):

Here, we use the word def. In Python, def stands for define because we are defining a function. When Python sees def it is expecting that what follows will follow the syntax of a function.

The next component in line one is adding_one. This is the name of the function. The print function’s name, as you may expect, is print. This is what you will use when you want to call your function later in your script.

Next, we see (number). A python function name must be followed by parentheses. Inside your parentheses, you can place one or multiple parameters. These are optional. Parameters allow you to pass arguments to a function. These arguments can be used by the function to perform some sort of action by either manipulating that parameter or using it in some capacity to do some task. In the case of our function, the only parameter is number. If you are creating multiple parameters, these should be separated by a comma, as we will see below. Notice that parameters do not have quotation marks around them. This is because they are not strings, rather parameters that will function like variables inside the code of the function.

Finally, in this first line, we see :. This tells Python two things. First, the syntax for defining the function is complete and that the next line will be indented within which the code for your function will sit. This : and the indentation are mandatory.

The next line of code is:

x = number+1

This is the code of the function. Here we are creating a variable within the function called x which will be the result of the number passed to the function and 1.

The final portion of the cell is

return x

This will return for the user x that we created temporarily in the function.

Let’s now call this function in the cell bellow to see how it works.

result = adding_one(1)

In the code above, we have created a new object, result. This will be the result of our function. Like all objects, it can be named whatever you like, save for the forbidden object names in Python.

We then call the function, adding_one and pass a single argument to it, an integer. In this case, 1.

I want you to take a moment and try and guess what result will be. Go ahead and try and recreate this code in your own notebook, or, if you are using this textbook with the built-in Binder environment, create a new cell below and print off “result”.

print(result)
Hide code cell output
2

Did you guess correctly? If so, great. If not, that’s okay. Why do you think your answer was wrong? Do you understand why? These are some of the core questions you should be asking yourself at this moment.

If we give the function the number 1, then our output will be 2 Likewise, if we do this with 3, we will get 4. But what would happen if we tried to pass the string "one"?

bad_result = adding_one("one")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [8], in <cell line: 1>()
----> 1 bad_result = adding_one("one")

Input In [2], in adding_one(number)
      1 def adding_one(number):
----> 2     x = number+1
      3     return x

TypeError: can only concatenate str (not "int") to str

We receive an error. This error message allows us to debug the problem. It is a TypeError, meaning we tried to use the wrong object type. We know that the error occurs in line 2 of our cell, x = number+1. Why do you think we received this error?

If you said because "one" is a string and you cannot add a string by the integer 1, then you would be right. If we were writing this program for ourselves, we would know this and never try to pass a string to the function, but what if someone else is trying to use our code and does not know precisely what it is supposed to do? In Python, we can pass in some key information to help our users.

4.1.3. Docstrings#

To help users, we can provide them with a long string at the start of the function that explains what the function does. This is known as a docstring. Let’s create a new function called adding_two with a docstring. This function will do the same as our earlier function, but add two to number and return that result.

def adding_two(number):
    """
    This function expects an integer and will return that integer plus 2.
    """
    x = x+2
    return x

Notice that the docstring is wrapped around three " at the beginning and end of the string. I can access this docstring by calling the function and using .__doc__.

print(adding_two.__doc__)
    This function expects an integer and will return that integer plus 2.
    

4.1.4. Functions with Multiple Arguments#

In the above section, we saw a simple function that had a single argument. In Python, you can assign as many arguments you want to a function. Let’s try to make a slightly different function that adds two numbers together, each supplied by the user.

def my_function2(number1, number2):
    x = number1+number2
    return x

Notice that my_function2 is the precise same as the function above except we have two arguments, “number1” and “number2”. Also, in the function x is the result of number1 plus number2.

Now, let’s try and use this function by passing two numbers to it: 1 and 3.

result2 = my_function2(1,3)
print (result2)
4

Yay! We got the result we desired. These are known as positional arguments because we are relying on the position of the argument in the function’s () section. Let’s modify this function slightly again so that you can see what I mean.

def my_function3(number1, number2):
    print ("Number 1 is ", number1)
    print ("Number 2 is ", number2)
my_function3(1, 3)
Number 1 is  1
Number 2 is  3

Notice that I have deleted the return line. This is because this function does not need to return anything to the user. Instead, it’s sole purpose is to simply print off what the two arguments are. Let’s reverse the order of our arguments now.

my_function3(3, 1)
Number 1 is  3
Number 2 is  1

Because these are positional arguments, we are dependent upon the position of the arguments to assign them correctly. We can get around this by specifying which argument we want to assign things to, thus avoiding the reliance on the order in which we pass the arguments to the function. Check out the code below to see this in action.

my_function3(number2=3, number1=1)
Number 1 is  1
Number 2 is  3

4.1.5. Keyword Argument#

Sometimes when we create a function, we want to make it optional for the user to pass an argument. In these instances, we will create what is known as a keyword argument, something that is set to a default. In the function below, we want to give the user the option to pass a last name to the function. Notice, though, it defaults to “Mattingly”.

def add_surname(first_name, last_name = "Mattingly"):
    print (first_name, last_name)
add_surname("William")
William Mattingly

That worked well, but a user still has the ability to change the last_name object.

add_surname("William", "Smith")
William Smith

4.1.6. Keyword Arbitrary Arguments#

In rare instances, you will not know precisely how many arguments a user will need to pass to your function, so you want to give them the option to pass as many as they wish. In these instances, you will use what are known as arbitrary arguments. You assign these with an * before the argument name.

def print_names(*names):
    for name in names:
        print (name)
print_names("William", "Marge", "Sally", "Alex")
William
Marge
Sally
Alex

Notice that the function has allowed the user to pass as many arguments as they wish to the function. In my entire time coding, I have only used an arbitrary argument a handful of times, but it is important to keep it in the back of your mind in case you are ever in that situation.

4.1.7. Conclusion#

Hopefully, you now have a basic understanding about what functions are and how they work in practice. I recommend spending a few hours playing around with some basic functions to do some tasks you need to perform on your own personal data. If you get stuck, try Googling your question. You would be surprised how many responses are available. Pay particular attention to StackOverflow responses.

4.1.8. Answer for Result#

print (result)
2