*This notebook contains an excerpt from the Python Programming and Numerical Methods - A Guide for Engineers and Scientists, the content is also available at Berkeley Python Numerical Methods.*

*The copyright of the book belongs to Elsevier. We also have this interactive book online for a better learning experience. The code is released under the MIT license. If you find this content useful, please consider supporting the work on Elsevier or Amazon!*

< Chapter 3 Functions | Contents | 3.2 Local Variables and Global Variables >

# Function Basics¶

In programming, a **function** is a sequence of instructions that performs a specific task. A function is a block of code that can run when it is called. A function can have **input arguments**, which are made available to it by the user, the entity calling the function. Functions also have **output parameters**, which are the results of the function that the user expects to receive once the function has completed its task. For example, the function *math.sin* has one input argument, an angle in radians, and one output argument, an approximation to the sin function computed at the input angle (rounded to 16 digits). The sequence of instructions to compute this approximation constitute the **body of the function**, which until this point has not been shown.

We saw many built-in Python functions already, such as *type*, *len*, and so on. Also we saw the functions from some packages, for example, *math.sin*, *np.array* and so on. Do you still remember how could we call and use these functions.

**TRY IT!** Verify that *len* is a built-in function using the *type* function.

```
type(len)
```

```
builtin_function_or_method
```

**TRY it!** Verify that *np.linspace* is a function using the *type* function. And find how to use the function using the question mark.

```
import numpy as np
type(np.linspace)
```

```
function
```

```
np.linspace?
```

## Define your own function¶

We can define our own functions. A function can be specified in several ways. Here we will introduce the most common way to define a function which can be specified using the keyword *def*, as showing in the following:

```
def function_name(argument_1, argument_2, ...):
'''
Descriptive String
'''
# comments about the statements
function_statements
return output_parameters (optional)
```

We could see that defining a Python function need the following two components:

**Function header:**A function header starts with a keyword*def*, followed by a pair of parentheses with the input arguments inside, and ends with a colon (:)**Function Body:**An indented (usually four white spaces) block to indicate the main body of the function. It consists 3 parts:*Descriptive string:*A string that describes the function that could be accessed by the*help()*function or the question mark. You can write any strings inside, it could be multiple lines.*Function statements:*These are the step by step instructions the function will execute when we call the function. You may also notice that there is a line starts with ‘#’, this is a comment line, which means that the function will not execute it.*Return statements:*A function could return some parameters after the function is called, but this is optional, we could skip it. Any data type could be returned, even a function, we will explain more later.

**TIP!** When your code becomes longer and more complicated, comments help you and those reading your code to navigate through it and understand what you are trying to do. Getting in the habit of commenting frequently will help prevent you from making coding mistakes, understand where your code is going when you write it, and find errors when you make mistakes. It is also customary to put a description of the function, author, and creation date in the descriptive string under the function header, even though it is optional (you could skip the descriptive string). We highly recommend that you comment heavily in your own code.

**TRY IT!** Define a function named *my_adder* to take in 3 numbers and sum them.

```
def my_adder(a, b, c):
"""
function to sum the 3 numbers
Input: 3 numbers a, b, c
Output: the sum of a, b, and c
author:
date:
"""
# this is the summation
out = a + b + c
return out
```

**WARNING!** If you don’t indent you code for defining function, you will get an **IndentationError**.

```
def my_adder(a, b, c):
"""
function to sum the 3 numbers
Input: 3 numbers a, b, c
Output: the sum of a, b, and c
author:
date:
"""
# this is the summation
out = a + b + c
return out
```

```
File "<ipython-input-5-e6a61721f00e>", line 8
"""
^
IndentationError: expected an indented block
```

**TIP!** Type 4 white spaces is one level of indentation, you can have deeper level indentation when you have nested function or if-statement (you will see this in next chapter). Also, sometimes you need to indent or un-indent a block of code. You can do this by first select all the lines in the code block, and press *Tab* and *Shift+Tab* to increase or decrease one level of indentation.

**TIP!** Build good coding practices by giving variables and functions descriptive names, commenting often, and avoiding extraneous lines of code.

For contrast, consider the following function that performs the same task as my_adder but is constructed poorly. As you can see, it is extremely difficult to see what is going on and the intention of the author.

**EXAMPLE**: Poor representation of my_adder.

```
def abc(a, s2, d):
z = a + s2
z = z + d
x = z
return x
```

Functions must conform to a naming scheme similar to variables. They can only contain alphanumeric characters and underscores, and the first character must be a letter.

**TIP!** Conventionally as the variable names, function names should be lowercase, with words separated by underscores as necessary to improve readability.

**TIP!** It is good programming practice to save often while you are writing your function. In fact many programmers report saving using the shortcut ctrl+s (PC) or cmd+s (Mac) every time they stop typing!

**TRY IT!** Use your function my_adder to compute the sum of a few numbers. Verify that the result is correct. Try calling the help function on my_adder.

```
d = my_adder(1, 2, 3)
d
```

```
6
```

```
d = my_adder(4, 5, 6)
d
```

```
15
```

```
help(my_adder)
```

```
Help on function my_adder in module __main__:
my_adder(a, b, c)
function to sum the 3 numbers
Input: 3 numbers a, b, c
Output: the sum of a, b, and c
author:
date:
```

**WHAT IS HAPPENING?** First recall that the assignment operator works from right to left. This means that *my_adder(1,2,3)* is resolved before the assignment to *d*.

Python finds the function

*my_adder*.*my_adder*takes the first input argument value 1 and assigns it to the variable with name*a*(first variable name in input argument list).*my_adder*takes the second input argument value 2 and assigns it to the variable with name*b*(second variable name in input argument list).*my_adder*takes the third input argument value 3 and assigns it to the variable with name*c*(third variable name in input argument list).*my_adder*computes the sum of*a*,*b*, and*c*, which is 1 + 2 + 3 = 6.*my_adder*assigns the value 6 to the variable*out*.*my_adder*outputs the value contained in the output variable*out*, which is 6.*my_adder(1,2,3)*is equivalent to the value 6, and this value is assigned to the variable with name*d*.

Python gives the user tremendous freedom to assign variables to different data types. For example, it is possible to give the variable x a dictionary value or a float value. In other programming languages this is not always the case, you must declare at the beginning of a session whether x will be a dictionary or a float type, and then you’re stuck with it. This can be both a benefit and a drawback (more on this in Chapter XXX). For instance, *my_adder* was built assuming that the input arguments were numerical types, either int or float. However, the user may accidentally input a list or string into *my_adder*, which is not correct. If you try to input a non-numerical type input argument into *my_adder*, Python will continue to execute the function until something goes wrong.

**TRY IT!** Use the string ‘1’ as one of the input arguments to *my_adder*. Also use a list as one of the input arguments to *my_adder*.

```
d = my_adder('1', 2, 3)
```

```
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-10-245d0f4254a9> in <module>
----> 1 d = my_adder('1', 2, 3)
<ipython-input-4-72d064c3ba7a> in my_adder(a, b, c)
9
10 # this is the summation
---> 11 out = a + b + c
12
13 return out
TypeError: must be str, not int
```

```
d = my_adder(1, 2, [2, 3])
```

```
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-11-04f0428ffc51> in <module>
----> 1 d = my_adder(1, 2, [2, 3])
<ipython-input-4-72d064c3ba7a> in my_adder(a, b, c)
9
10 # this is the summation
---> 11 out = a + b + c
12
13 return out
TypeError: unsupported operand type(s) for +: 'int' and 'list'
```

**TIP!** Remember to read the errors that Python gives you. They usually tell you exactly where the problem was. In this case, the error says *—> 11 out = a + b + c*, meaning there was an error in my_adder on the 11th line. The reason there was an error is **TypeError**, because *unsupported operand type(s) for +: ‘int’ and ‘list’*, which means that we couldn’t add int and list.

At this point, you do not have any control over what the user assigns your function as input arguments and whether they correspond to what you intended those input arguments to be. So for the moment, write your functions assuming that they will be used correctly. You can help yourself and other users use your function correctly by commenting your code well.

You can compose functions by assigning function calls as the input to other functions. In the order of operations, Python will execute the innermost function call first. You can also assign mathematical expressions as the input to functions. In this case, Python will execute the mathematical expressions first.

**TRY IT!** Use the function *my_adder* to compute the sum of \(sin ({\pi})\), \(cos ({\pi})\), and \(tan ({\pi})\). Use mathematical expressions as the input to *my_adder* and verify that it performs the operations correctly.

```
d = my_adder(np.sin(np.pi), np.cos(np.pi), np.tan(np.pi))
d
```

```
-1.0
```

```
d = my_adder(5 + 2, 3 * 4, 12 / 6)
d
```

```
21.0
```

```
d = (5 + 2) + 3 * 4 + 12 / 6
d
```

```
21.0
```

Python functions can have multiple output parameters. When calling a function with multiple output parameters, you can place the multiple variables you want assigned separated by commas. The function essentially will return the multiple result parameters in a tuple, therefore, you could unpack the returned tuple. Consider the following function (note that it has multiple output parameters):

```
def my_trig_sum(a, b):
"""
function to demo return multiple
author
date
"""
out1 = np.sin(a) + np.cos(b)
out2 = np.sin(b) + np.cos(a)
return out1, out2, [out1, out2]
```

**TRY IT!** Compute the function *my_trig_sum* for *a=2* and *b=3*. Assign the first output parameter to the variable *c*, the second output parameter to the variable *d*, and the third parameter to the variable *e*.

```
c, d, e = my_trig_sum(2, 3)
print(f"c ={c}, d={d}, e={e}")
```

```
c =-0.0806950697747637, d=-0.2750268284872752, e=[-0.0806950697747637, -0.2750268284872752]
```

If you assign the results to one variable, you will get a tuple that has all the output parameters.

**TRY IT!** Compute the function *my_trig_sum* for *a=2* and *b=3*. Verify the output is a tuple.

```
c = my_trig_sum(2, 3)
print(f"c={c}, and the returned type is {type(c)}")
```

```
c=(-0.0806950697747637, -0.2750268284872752, [-0.0806950697747637, -0.2750268284872752]), and the returned type is <class 'tuple'>
```

A function could be defined without an input argument and returning any value. For example:

```
def print_hello():
print('Hello')
```

```
print_hello()
```

```
Hello
```

**Note!** Even there is no input argument, when you call the function, you still need the parentheses.

For the input of the argument, we can have the default value as well. See the following example.

**EXAMPLE:** Run the following function with and without an input.

```
def print_greeting(day = 'Monday', name = 'Qingkai'):
print(f'Greetings, {name}, today is {day}')
```

```
print_greeting()
```

```
Greetings, Qingkai, today is Monday
```

```
print_greeting(name = 'Timmy', day = 'Friday')
```

```
Greetings, Timmy, today is Friday
```

```
print_greeting(name = 'Alex')
```

```
Greetings, Alex, today is Monday
```

We can see that, if we give a value to the argument when we define the function, this value will be the default value of the function. If the user doesn’t provide an input to this argument, then this default value will be used during calling of the function. Besides, the order of the argument is not important when calling the function if you provide the name of the argument.

< Chapter 3 Functions | Contents | 3.2 Local Variables and Global Variables >