# Functions

## Functions: what and why?

A *function* is a re-usable code block, typically with a name. The main goal of functions is to avoid duplication of code. Another important goal of functions is to divide code into parts with clearly defined functions; doing so can drastically improve the readability of your code.

Say that you want calculate the length of the long side `c`

of a right triangle with short sides `a`

and `b`

, using Pythagoras' theorem. And say that you have a list (`some_triangles`

) of `a, b`

tuples, which you can easily iterate through to determine the corresponding long sides `c`

.

```
some_triangles = (1, 1), (1, 2), (2, 2)
for a, b in some_triangles:
c = (a ** 2 + b ** 2) ** .5
print('a = {0}, b = {1}, c = {2}'.format(a, b, c))
```

**Output:**

```
a = 1, b = 1, c = 1.4142135623730951
a = 1, b = 2, c = 2.23606797749979
a = 2, b = 2, c = 2.8284271247461903
```

So far there is no code duplication, and hence no real need for a function. But now say that you want to do *almost* the same thing again, for another `list`

(`more_triangles`

) of `a, b`

tuples. To do this without a function, we would have to copy-paste our code, resulting in code duplication:

```
some_triangles = (1, 1), (1, 2), (2, 2)
for a, b in some_triangles:
c = (a ** 2 + b ** 2) ** .5
print('a = {0}, b = {1}, c = {2}'.format(a, b, c))
more_triangles = (3, 3), (3, 4), (4, 4)
for a, b in more_triangles:
c = (a ** 2 + b ** 2) ** .5
print('a = {0}, b = {1}, c = {2}'.format(a, b, c))
```

**Output:**

```
a = 1, b = 1, c = 1.4142135623730951
a = 1, b = 2, c = 2.23606797749979
a = 2, b = 2, c = 2.8284271247461903
a = 3, b = 3, c = 4.242640687119285
a = 3, b = 4, c = 5.0
a = 4, b = 4, c = 5.656854249492381
```

When you see this kind of code duplication, then you know that a function is likely to improve your code. Let's see, step by step, how we can accomplish this.

## def: defining a function

A function is defined with the `def`

statement, followed by the name of the function and parentheses. Like any statement that is followed by an indented code block, the statement ends with a colon.

Let's define a `pythagoras()`

function that (for now) calculates the long side (`c`

) of a triangle with short sides (`a`

and `b`

) of length 1, and prints out the result. We execute the function by calling its name followed by parentheses.

```
def pythagoras():
a = b = 1
c = (a ** 2 + b ** 2) ** .5
print('a = {0}, b = {1}, c = {2}'.format(a, b, c))
pythagoras() # Call the function
```

**Output:**

```
a = 1, b = 1, c = 1.4142135623730951
```

## Arguments and return values

### Function arguments

The `pythagoras()`

function defined above is not very useful, because it lacks flexibility. It calculates the long side `c`

, but *only* for short sides `a = 1`

and `b = 1`

. To make the function more flexible, you can add *arguments*. That is, you pass one or more variables to the function, and the function then performs some operation on or with these variables.

```
def pythagoras(a, b):
c = (a ** 2 + b ** 2) ** .5
print('a = {0}, b = {1}, c = {2}'.format(a, b, c))
pythagoras(1, 1)
```

**Output:**

```
a = 1, b = 1, c = 1.4142135623730951
```

### Default function arguments (keywords)

Function arguments can have default values, so that you can, but do not have to, pass these arguments. Such arguments with default values are called *keywords*. We can redefine `pythagoras()`

such that the `a`

and `b`

arguments are keywords with a default value of `1`

.

```
def pythagoras(a=1, b=1):
c = (a ** 2 + b ** 2) ** .5
print('a = {0}, b = {1}, c = {2}'.format(a, b, c))
pythagoras()
```

**Output:**

```
a = 1, b = 1, c = 1.4142135623730951
```

### Return values

Many functions also have *return values*, that is, they communicate a value back to where they were called from. We can redefine `pythagoras()`

such that it doesn't print the length of the long side directly, but rather `return`

s it.

```
def pythagoras(a=1, b=1):
return (a ** 2 + b ** 2) ** .5
a = b = 1
c = pythagoras(a, b)
print('a = {0}, b = {1}, c = {2}'.format(a, b, c))
```

**Output:**

```
a = 1, b = 1, c = 1.4142135623730951
```

## Docstrings

A documentation string, or *docstring*, is a `str`

that directly follows the `def`

line. This allows you to describe what the function does. In most cases, a docstring will be a multiline string. It is best practice to document all but the most trivial functions with a clear docstring!

```
def pythagoras(a=1, b=1):
"""Returns the length of the long side of a right triangle
given two short sides a and b.
"""
return (a ** 2 + b ** 2) ** .5
```

## Functions that call functions

Most programs consist of functions that call other functions that call yet other functions, etc. Returning to the example that we started with, we could rewrite this code to make use of functions, thus avoiding duplication and making the logic of the code clearer.

```
def pythagoras(a=1, b=1):
"""Returns the length of the long side of a right triangle
given two short sides a and b.
"""
return (a ** 2 + b ** 2) ** .5
def print_long_sides(short_sides):
"""Takes a list of (a, b) tuples, corresponding to the short
sides of a right triangle, and prints out the corresponding
long side (c).
"""
for a, b in short_sides:
c = pythagoras(a, b)
print('a = {0}, b = {1}, c = {2}'.format(a, b, c))
some_triangles = (1, 1), (1, 2), (2, 2)
more_triangles = (3, 3), (3, 4), (4, 4)
print_long_sides(some_triangles)
print_long_sides(more_triangles)
```

**Output:**

```
a = 1, b = 1, c = 1.4142135623730951
a = 1, b = 2, c = 2.23606797749979
a = 2, b = 2, c = 2.8284271247461903
a = 3, b = 3, c = 4.242640687119285
a = 3, b = 4, c = 5.0
a = 4, b = 4, c = 5.656854249492381
```

## Exercises

### Factorial using for

The factorial (`!`

) of a positive integer number is the product of all numbers from 1 up to and including the number itself. So `3! == 3 × 2 × 1`

. By convention, `0! == 1`

. The factorial of negative numbers is undefined.

Define a function that takes a number as an argument, and returns the factorial for that number. The function can assume that the input is a non-negative integer. Use a `for`

loop inside the function.

### Factorial using recursion

When a function calls itself, this is called *recursion*. Define a factorial function that does *not* use a `for`

loop, but uses recursion.