User defined functions

Defining our own functions in python is simple. Like loops and if-else statements, one line is reserved for the definition and then all of the instructions that need to be included with the function are indented one level from the function definition. The function definition ends when indentation returns to the previous level. Also, function definitions should include a return statement. A simple example:

[1]:
def addone(a):
    return(a+1)

addone() is a function that takes 1 argument and then returns a single value. Inside the function definition, whatever value that we passed via the argument gets stored inside the function using the variable a. Remember scope? a only has scope inside of the function:

[2]:
print(a)
-------------------------------------------------------------------
NameError                         Traceback (most recent call last)
<ipython-input-2-bca0e2660b9f> in <module>
----> 1 print(a)

NameError: name 'a' is not defined

I can use this function by defining a variable in the main program:

[3]:
mynumber = 15
mynewnumber = addone(mynumber)
print(mynewnumber)

16

To restate, defining our own functions allows us to reuse code in several different locations in our programs.

[4]:
anothernumber = 10522
anothernewnumber = addone(anothernumber)
print(anothernewnumber)
10523

As was mentioned in the previous part of the lesson, python is a little weird regarding scope inside a function. For example, in other languages, trying to print a variable that is defined outside a function would result in an error. Here it doesn’t:

[5]:
def addtwo(b):
    print(number3)
    return(b+2)

number3 = 59
newnumber3 = addtwo(number3)
print(newnumber3)

59
61

In this example, the variable b has taken on the value of number3 since number3 was passed as an argument when I called the addtwo() function. So, I shouldn’t try to use number3 inside the function definition. Still, Python allows it. This all works because if you use the same variable inside a function as you use outside a function, even though they have the same name, they are physically different variables:

[6]:
def addtwo(b):
    number3 = 50
    print("number3 inside function: {}",number3)
    return(b+2)

number3 = 59
newnumber3 = addtwo(number3)
print("number3 outside function: {}",number3)
number3 inside function: {} 50
number3 outside function: {} 59

Again, we have 2 number3 variables. But they are really two different variables (physically, they point to two different places in the computer’s memory). One has scope inside the function definition, one has scope outside. If you think about it, this behavior makes sense. There are countless functions that we might use. It would be extrememly prohibitive if we were unable to use variable names that were defined inside those functions.

I mentioned at the top that functions should return a value. This isn’t a strict rule:

[7]:
def printmessage(message):
    print(message)

printmessage("Tacos really are the perfect food.")
Tacos really are the perfect food.

My function doesn’t return anything, but it still does something. Return statements are necessary for getting information out of a function. If your function doesn’t have to give any information back, then you don’t necessarily need one. However, many people would argue that it is good practice to always include a return value. So, you might do (or see) something like this:

[8]:
def printmessage(message):
    print(message)
    return 0

printmessage("Just like pizza.")
Just like pizza.
[8]:
0

A value of zero basically means “no error”. So, doing this is a way to help handle errors that crop up in your program. Maybe if something goes wrong with the message, your function would return a value of 1, which would trigger a message to the user or something like that.

Functions with multiple arguments

Including more than one argument is easy. Simple add commas:

[9]:
def addtwonumbers(a,b):
    return(a+b)

num1 = 6
num2 = 13
print(addtwonumbers(num1,num2))
19

In this case, a and b are positional arguments. a gets the first value that is passed in the function call while b gets the second value. So what about keyword arguments? Those are pretty easy too:

[10]:
def twonumbermath(a,b,operation='plus'):
    if operation == 'plus':
        return(a+b)
    if operation == 'minus':
        return(a-b)

num1 = 8
num2 = 12
print(twonumbermath(num1,num2,operation='plus'))
print(twonumbermath(num1,num2,operation='minus'))
print(twonumbermath(num1,num2))

20
-4
20

I’ve included one keyword arguement, using the keyword “operation”. I can choose whatever keyword I want since I’m defining the function. When I call the function using the keyword, inside the function the variable operation takes on the value of whatever I assigned to it in the function call. But here, I’ve also set a default value of ‘plus’ in the function definition itself. If I don’t include the keyword in the function call, the function still works and operation gets assigned a value of “plus”.