# 4. Functions¶

So far you have being using existing functions like list and type, but how about defining your own functions? Turns out that’s very easy with Python:

```
In [1]:
```

```
def addmult(x, y):
adding = x + y
multiplying = x * y
return multiplying / adding
z = addmult(10.2, 30.0)
z
```

```
Out[1]:
```

```
7.611940298507462
```

That’s it, but just notice the spacing after the first line of the function. That’s the way Python functions (and as we’ll see, conditionals and loops) works: using indentation. A single whitespace or tab is enough, however, the most common usage is to have 4 whitespace in Python scripts.

## 4.1. Return value¶

Functions in Python can either return you “something" like the one above, or just return “nothing":

```
In [2]:
```

```
def print_and_return(x, y):
adding = x + y
multiplying = x * y
print(multiplying)
return
print_and_return(10.2, 30.0)
```

```
306.0
```

In the above case, you actually didn’t need the return, statement; once a function goes to its last line (i.e.: indentation decreases), it will return automatically:

```
In [3]:
```

```
def print_and_return(x, y):
adding = x + y
multiplying = x * y
print(multiplying)
print_and_return(10.2, 30.0)
```

```
306.0
```

But, you could use return to make the function return immediately:

```
In [4]:
```

```
def print_and_return():
print("This will be printed")
return
print("This will never be printed")
print_and_return()
```

```
This will be printed
```

This will prove itself useful together with conditionals in the next section.

### 4.1.1. An optional lookahead¶

In case of a function returning “nothing", you actually get something: a None object which happens to have Nonetype type:

```
In [5]:
```

```
def print_and_return(x, y):
adding = x + y
multiplying = x * y
print(multiplying)
somevar = print_and_return(10.2, 30.0)
somevar is None
somevar
type(somevar)
```

```
306.0
```

```
Out[5]:
```

```
NoneType
```

### 4.1.2. Exercise¶

What’s wrong with the code bellow, why does the doesn’t z equals 60.0?:

```
In [6]:
```

```
def mult(x, y):
multiplying = x * y
multiplying
z = mult(2.0, 30.0)
z
```

## 4.2. Variable Scope¶

Here there’s three points to make, first that variables defined inside a function only exist within the scope of the function, so the code below:

```
def addmult(x, y):
adding = x + y
ddf
multiplying = x * y
return multiplying / adding
ddf = 342
addmult(10.2, 30.0)
adding
```

Will give you an error message:

```
NameError: name 'adding' is not defined
```

Second, that once you declare a variable inside a function it will not overwrite a variable defined outside of it which happens to have the same name, so:

```
In [7]:
```

```
def somefunc():
somevar = 421
print("Value inside somefunc()", somevar)
return
somevar = 123
somefunc()
print("Value outside somefunc()", somevar)
```

```
Value inside somefunc() 421
Value outside somefunc() 123
```

Third, if you did not defined the variable inside the function, but it does exists outsize of it, then Python will use it, so:

```
In [8]:
```

```
def somefunc():
print("Value inside somefunc()", something)
return
something = 482
somefunc()
print("Value outside somefunc()", something)
```

```
Value inside somefunc() 482
Value outside somefunc() 482
```

So here’s a summary of it: when you ask Python to get you a variable inside a function, it will first attempt to get the one defined inside the function and then if not found, it will try outside the function.

If you ask it to set a variable, it will always set it on local scope of the function (it will not overwrite a variable defined outsize with the same name).

## 4.3. Named arguments and default values¶

To make things easier, Python also accepts you to pass the arguments of a function by name:

```
In [9]:
```

```
def somefunc(x, y, z):
adding = 2 * x + y + z
return adding * z
somefunc(x=10.2, y=30.0, z=3.1)
somefunc(y=30.0, z=3.1, x=10.2)
somefunc(10.2, y=30.0, z=3.1)
somefunc(10.2, z=3.1, y=30.0)
somefunc(10.2, y=3.1, z=30.0)
```

```
Out[9]:
```

```
1605.0
```

You can also provide some default value for an argument (effectively making it optional):

```
In [10]:
```

```
def somefunc(x, y, z=1):
adding = 2 * x + y + z
return adding * z
somefunc(10.2, 30.0)
somefunc(10.2, 30.0, 1)
somefunc(y=30.0, x=10.2)
somefunc(y=30.0, x=10.2, z=1.4)
somefunc(10.2, 30.0, 1.4)
```

```
Out[10]:
```

```
72.52
```

## 4.4. Lambda and function redefinitions¶

In Python, a function can also be seems as a object or as variable like any other else (with the special property of being callable), and as such, you can redefine it as usual:

```
In [11]:
```

```
def somefunc(x, y):
adding = 2 * x + y
return adding * 4
somefunc(10.2, 30.0)
somefunc = "Something else"
def somefunc(x):
return x*2
somefunc(43.2)
```

```
Out[11]:
```

```
86.4
```

You can also create a function in a single line using the lambda syntax:

```
In [12]:
```

```
somefunc = lambda x, y: 2 * x + y
somefunc(10.2, 30.0)
```

```
Out[12]:
```

```
50.4
```

This will prove itself useful in the future…