# User-Defined Functions

## Contents

# User-Defined Functions#

In the case where we want to compute something multiple times but there is no built-in function to rely on, we can write our own function! Replacing multiple lines of code with a function allows seemless reuse of a process or computation.

The general format for defining a function is given below.

```
def function_name(input_arguments):
""" Documentation on what your function does """
body of function
return output
```

The

`def`

keyword indicates defining a functionfunction_name: We can name our function however we please

input_arguments: We decide how many values our function takes as input

docstring: We document the key characteristics of our function by using a string - typically triple quotes

body of fuction: We perform some computation in the indented body

output: The output is returned

When might we use functions? Suppose we’re in the United States working on the Mars probe launch of 1999. We accidentally sent data to our metric loving friends with measurements provided in inches. Let’s make sure the Mars probe doesn’t get lost in space and define a function to convert inches to centimeters before sending that data over.

```
def convert_inches_to_cm(x_inches):
"""Takes input of inches and converts to centimeters"""
x_cms = x_inches * 2.54
return x_cms
```

Above we created a function that converts inches to centimeters. Note the input argument of *x_inches* is taken in and used in the indented body of the function, and the new variable x_cms is returned. But the function does not *do* anything unless we call it. Now that it is defined, we call it exactly as we would a built-in function - with our function_name followed by parentheses into our input.

```
convert_inches_to_cm(5)
```

```
12.7
```

Notice we have created this new variable *x_cms* in our function *convert_inches_to_cm*. However when we try to print this variable name *outside* the defined function, as below, we get an error.

```
x_cms
```

```
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[3], line 1
----> 1 x_cms
NameError: name 'x_cms' is not defined
```

This is because of the **scope** of a variable, or where a variable is visible to other code. If we define a variable inside the body of a function, it is visible only inside that function.

And remember the docstring we wrote when defining our function? That is accessible to us, or others reading our code, with the help function!

```
help(convert_inches_to_cm)
```

```
Help on function convert_inches_to_cm in module __main__:
convert_inches_to_cm(x_inches)
Takes input of inches and converts to centimeters
```

# Return vs Print#

Another element to be aware of is the *return* statement at the end of a function. Creating a similar function that ends with *print* instead of *return*, and calling it on an input of 5, as below, it might not be obvious there is a difference.

```
def convert_inches_to_cm_print(x_inches):
"""Takes input of inches and converts to centimeters"""
x_cms = x_inches * 2.54
print(x_cms)
```

```
convert_inches_to_cm(5)
```

```
12.7
```

```
convert_inches_to_cm_print(5)
```

```
12.7
```

But keep in mind we are working in an interactive Python environment, where the last line of code is printed by default, thus it appears that both functions print their output when in actuality only *convert_inches_to_cm_print* prints the output.

So, what is the difference between the two function outputs above? The function *return* statement allows us to store and reuse the output of a function, while the *print* statement prints the output exactly when the function is called, and the output is not stored. Since the main purpose of a function is to store the function’s output for later potential use in our code, the *return* statement is very important and powerful.

Therefore the *return* statement in our first function *convert_inches_to_cm*, allows for variable assignment to the output of the function. If we try to assign the output of function *convert_inches_to_cm_print* to a new variable, we find there is no variable to assign.

```
new_var = convert_inches_to_cm(5)
print(new_var)
```

```
12.7
```

```
new_var2 = convert_inches_to_cm_print(5)
print(new_var2)
```

```
12.7
None
```

Notice the value of 12.7 is printed in both cells above. The first cell prints 12.7 because we tell it to print variable *new_var*, the second cell prints 12.7 because it is running through the body of the function, which says to print *x_cms*, but the variable *new_var2* is a NoneType object that has no value.

# Functions with Multiple Arguments#

Let’s try another example. Here we write a function that takes two values as input and computes the sum of squares.

```
def sum_squares(x, y):
""" Takes two numbers x and y and returns the sum of squares"""
z = x**2 + y**2
return z
```

Here the function body is defined in terms of x and y, which we must specify when calling the function.

```
sum_squares(2,1)
```

```
5
```

If we wanted to extend the function `sum_squares`

to calculate the sum of cubes or the sum of any given powers we can make the power itself an argument to the function. Further, we can assume a default option for the exponent to be 2. We do this by assigning our variable a value, in this case a number, in the input_arguments of the function definition. We then have the option to change this argument, or exclude it entirely and use the default option when calling the function.

```
def sum_powers(x, y, power=2):
""" Takes two numbers x and y and returns the sum of powers.
If not specified the default compute the sum of squares, that is power = 2 """
z = x**power + y**power
return z
```

Calling this function without the power argument gives the same outcome as `sum_squares`

, but when we include an additional argument of 3, we’re computing the sum of cubes!

```
sum_powers(2,1)
```

```
5
```

```
sum_powers(2,1,3)
```

```
9
```

# Argument Order#

The order of the arguments does matter when calling a function. If we switch order of inputs 1 and 3 we do get a different answer.

```
sum_powers(2,3,1)
```

```
5
```

If we want to more explicit about which variable in which in our input, we can call the function with the given function variables assigned. This allows us to rearrange the order of inputs without affecting the function output.

```
sum_powers(x=1, y=2, power=3)
```

```
9
```

```
sum_powers(x=1, power=3, y=2)
```

```
9
```

Functions are extremely important and useful methods to reuse, organize, and simplify code. We will see functions used throughout this textbook. As we explore more Python, we will find that functions are very versatile and can be applied to many different objects while streamlining repeatable processes.