FUNCTIONS

I. Basics of Functions

Functions are where all action is in Python and, really, every programming language. After mastering functions you will begin to be able to program a computer. Not bad, eh? Now you can brag to your all your friends and family and start boring them to tears with discussions of loop statements and nested scopes.

Function Analogy: I think of functions as little machines, like a food processor. You put some cabbage in your food processor and out comes cole slaw! Writing a function is like making a little programming maching you can use over and over again (this is the priniciple of "code reuse") for specific tasks like adding numbers together, searching for names in a dictionary or getting data from a file.

Here is an example of a function that performs a simple task of printing out an error message:

>>> error4 = "Error of type 4 made."

>>> def error_msg(x):
                print x

Let us break example down: the def command is a Python specific statement that says "the following words will be the name of the function". In this case the function is named error_msg. Then notice the parentheses; this is where the arguments will go (like the cabbage in the food processor).

In the case of error_msg the function takes one argument, indicated by the variable named x. Then the function processes whatever x is set to. In this case, it just prints the value of x. [Notice that after the parentheses, you have a colon just like if/elif, for and while statements.] Then, the guts of the function are indented. Whatever statements are indented after the colon are in the body of the function and belong to the function. Here is the generic function outline:

def <name>(arg1, arg2, ...argN):
        <statements>

Here is an example of the error_msg function at work:

>>> error4 = "Error of type 4 made."

>>> error_msg(error4)
Error type 4 made.

The first part just makes a new variable, error4, and assign a string to this variable name - an error message. Then, in the second part, we are calling the function. When the function is called, this is when we assign the value of the arguments. In this case we assign the value of error4 to x which is then processed by the function. Just like throwing the cabbage in the food processor!

What happens when you just type the following?

(1) >>> error_msg()

(2) >>> x

Wait a minute? What happened to my variable x? Should it not be equal to the value of error4?

It turns out that the variables you use for arguments are actually only available for the function and are "destroyed" after the function is run. The argument variables are said to be in the local scope of the function and only exist for the function. The variable error4, on the other hand is in the global scope and is available all the time once it is created in the interpreter.

This can cause some bizarre behavior, which makes sense if you think about it. Can you figure the following out?

>>> x = "No error"

>>> def error_msg(x):
                print x

>>> error_msg("spam")
spam

Type x in the interpreter. What does it equal to now?

Try the following:

>>> error_msg(4)

>>> error_msg([5,6,'spam'])

Remember that, Python can assign any type of object to a variable name. You do not have to declare what type of variable it is ahead of time. This is something that can cause occasional problems: Python will not throw you an error message when you put in the wrong value, so you need to make sure that your function has the correct type of input and output.

By the way, you can also write functions that take no arguments:

>>> def error_msg():
                print "Error made!"

>>> error_msg()
Error made!

II. Return Statements and Function Calls

Now we get a little more complicated and write a function with multiple arguments. The next function takes in two arguments and returns the sum of the values:

>>> def add2numbers(x,y):
                return x+y

>>> add2numbers(4,10)
14

Notice the return statement. When you call this function in the interpreter it will print the value to the screen just like a print function. But you can also return the value to a new variable. For instance:

>>> output = add2numbers(5,6)

>>> output
11

What is the value of output when you do the following?:

>>> output = add2numbers(3, 5.6)

Exercises:

(1) Write a function called times which multiplies 2 numbers and returns the results.

(2) Change the times function so that it takes in a 3rd argument and adds that to the product of the first two. What do you get when call times(2,10,5)?

III. Default Values

One very useful trick with functions is to set default values for the arguments in the function definition. This means that the function will always run even when you forget to put in values for the arguments when calling the function. Here is an example altering a previous function:

>>> def add2numbers(x=1,y=0):
                return x+y

>>> add2numbers() # Use the default values
1

>>> add2numbers(4,5)
9

What happens when you call the function with one value, say 10?

Try assigning an new variable spam = 5. Then call add2number(spam, 400). What do you get?

IV. Statements within Functions

Those were pretty simple functions, so now we can get a little more complicated by putting in some of those basic statements you learned in the previous lesson. Here is a function that uses an if/elif statment to check the value of a variable to see if it is a positive number:

>>> def check(x):
                if x > 0:
                        return 1 #true true
                else: return 0 #false


Call this function with the value 4 and then -4. What is the result?

Now let us try using a for loop within a function. The following function just searches through a list to find a particular name and, if found returns 1. 0 is returned if the name is not found.

>>> name_list = ['Gwen', 'Arthur', 'Lance']

>>> def find_name(name):
                for i in name_list:
                        if i == name:
                                return 1 #Found name
                        else: pass #What does this do?
                else: return 0 #Name not found


>>> find_name('Scott')
0

>>> find_name('Gwen')
1

Do you understand why the pass statement is there? What would happen if you put in a return 0 at that point instead of a pass statment? Try rewriting the function that way and calling find_name('Lance'). What happens?

IV. Functions within Functions

Just like with any Python object, once you make a function you can use it wherever you want, even within other functions!

Here is an example of a function that calls another function inside it (the check_em function you wrote earlier):

def divide_em(a, b):
        if check(b):
                return a/b
        else:
                print "Can't divide by 0."

>>> divide_em(8,4)
2

>>> divide_em(8,-4)
"Can't divide by 0."

What happens when you call divide_em(4,8)? How about divide_em(5,4)? How might you alter the function to take care of this problem? Hint: try using the built in function float(x).

Exercises:

(1) Write a function called codon_search that takes in a string argument (a DNA sequence in this case) and searches for a particular three letter codon string 'GAT'. First write the function so that it return a 1 (true) if 'GAT' is found in the DNA string. Then rewrite the function to return the number of times the string was found. Hints: Use a for loop and slice up the string into three letter bits.

(2) Write a function called phone_search that takes in two arguments. The first is a list of names and the second is a list of phone numbers. Return a dictionary in which the keys are the names and the values are the phone numbers. Try calling phone_search with the following lists and return the output to a variable named phone_book. Hint: Use two for loops, one within the other.

names = ['Smith', 'Miller', 'Jones']

p_numbers = ['555-1008', '555-2303', '555-7101']

How could you change the function so that it checked the length of the lists to see if they had the same number of elements? What built-in function would you use?