Lecture 13

Functions

Functions are sequences of instructions packaged together into a single statement. They can also be called "methods", "subroutines", "procedures", and "routines", depending on the programming language.

Why create functions?

Simple Functions

There are a lot of details with functions in Python. We'll start with the basics. The simplest form of a function is:

def functionName():
    statements to execute when function is called

The function header is the first line (starting with "def ...") and the function body are the statements to execute. The parentheses in the function header are necessary, even though they're empty for now. All of the statements to execute must be indented.

Here are two examples:

def print_HW():
    print("Hello, World!")

def print_HW_name():
    name = input("Please enter your name: ")
    while len(name) == 0:
        print("Your name cannot be blank.")
        name = input("Please enter your name: ")
    print("Hello, ", name, "!", sep="")

A function is not executed until it is called. So in the example above, nothing will print to the screen. To call a function, you use its name, followed by parentheses, e.g.:

Code Output
def print_HW():
    print("Hello, World!")

print_HW()
Hello, World!

Calling a function you wrote is no different than calling a function written by someone else (including the functions provided by Python).

The order matters when defining/declaring a function and calling it. You must first define a function, then call it. So the following will not work:

Code Output
print_HW()

def print_HW():
    print("Hello, World!")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'print_HW' is not defined

Scope

Variables created inside a function exist only in that function. Outside of that function, the variable is inaccessible. For example, this will not work:

Code Output
def print_HW_name():
    name = input("Please enter your name: ")
    while len(name) == 0:
        print("Your name cannot be blank.")
        name = input("Please enter your name: ")
    print("Hello, ", name, "!", sep="")

print_HW_name()
print(name, ", thank you for running this program.", sep='')
Please enter your name: Michael
Hello, Michael!
Traceback (most recent call last):
  File "<stdin>", line 9, in <module>
NameError: name 'name' is not defined

This will not work either:

Code Output
def get_name():
    name = input("Please enter your name: ")
    while len(name) == 0:
        print("Your name cannot be blank.")
        name = input("Please enter your name: ")

def print_HW_name():
    get_name()
    print("Hello, ", name, "!", sep="")

print_HW_name()
print(name, ", thank you for running this program.", sep='')
Please enter your name: Michael
Traceback (most recent call last):
  File "<stdin>", line 9, in <module>
NameError: name 'name' is not defined

Notice that it's possible to call one function from within another function! However, the get_name's variables are not accessible to print_HW_name.

The next two sections talk about how to move values between functions.

Passing Values into a Function

It is possible to write a function that requires functions to run. We've seen a lot of these already: int, float, and str all take a value and try to convert it into the appropriate type. The print and range functions also take values to work (they actually make use of some advanced argument passing techniques, which we'll talk about later).

To define a function that takes values, you list the variable names in the parentheses:

def functionName(param1, param2):
    statements to execute when function is called

You can have zero, one, two, or more variables in the parentheses. These variables are called "parameters". If you have more than one parameter, you separate them with commas.

To call a function that has parameters, you must supply values inside the parentheses. These values are called "arguments". For now, every parameter must have an argument.

Here are some examples:

Code Output
def greet_user(name):
    print("Hello, ", name, "!", sep="")

greet_user("Michael")
name = input("Please enter your name: ")
greet_user(name)
Hello, Michael!
Please enter your name: Donald
Hello, Donald!
Code Output
def greet_user2(greeting, name):
    print(greeting, ", ", name, "!", sep="")

greet_user2("Hello", "Michael")
name = input("Please enter your name: ")
greet_user2('Hi', name)
Hello, Michael!
Please enter your name: Donald
Hi, Donald!

One complication with parameters is that sometimes if you change the parameter in the function, that change shows up outside the function. Other times, it does not. There is a pattern to this, but it might not be obvious at first. The basic rule is that if you assign a new value to a parameter, the change does not show up outside the function. However, if you modify the value of a parameter, that change will show up outside. This is called "Pass by Reference". For example:

Code Output
def add_5(num):
    num = num + 5
    print("in add_5:", num)

x = 0
add_5(x)
print("after add_5:", x)
in add_5: 5
after add_5: 0
Code Output
def add_5_v2(values):
    values.append(5)
    print("in add_5_v2:", values)

nums = [0, 1, 2, 3, 4]
add_5_v2(nums)
print("after add_5_v2:", nums)
in add_5_v2: [0, 1, 2, 3, 4, 5]
after add_5_v2: [0, 1, 2, 3, 4, 5]

What do you think happens in the following program?

def to_upper(text):
    text.upper()

While you can use pass by reference to get values out of a function, the prefered way to do that is with the return statement.

return Statement

The return statement ends a function and (optionally) sends back a value to where the function was called from. It is possible to send back zero, one, or more values.

The simplest is returning no value. Here, you just use the return keyword:

Code Output
def print_even_odd(num):
    if num % 2 == 0:
        print('even')
        return
    
    print('odd')

print_even_odd(7)
print_even_odd(2)
odd
even

Notice that when 2 is passed in, we only see 'even' print out. That's because the return statement stops the function at that point and goes back to where the function was called from.

For functions without any return statement, Python inserts a return statement at the very end of the function. This return statement doesn't return any value (because Python wouldn't know what value to return.

Technically, the empty return statement does return a value. It returns the special value None. There's not much you can do with this value, but it does have its uses. This is one use: to return a value from a functions like this one (these are often called "void functions" or "void-returning functions", a term borrowed from other programming languages).

To return one value, you place the value after the return keyword:

Code Output
def is_even(num):
    if num % 2 == 0:
        return True
    
    return False

print(is_even_odd(7))
print(is_even_odd(2))

vals = [1,2,3,4,5]
if is_even(len(vals)):
    print('The middle value is:', vals[len(vals)/2])
else:
    below_mid = vals[(len(vals)-1)/2]
    above_mid = vals[(len(vals)+1)/2]
    mid = (below_mid + above_mid) / 2
    print('The list\'s length is even, so the average of the two middle-most values is:', mid)
False
True

Just like the empty return statement, the return statement with a value also stops the function and returns to where the function was called from.

What do you think will happen if the is_even function were written as:

def is_even(num):
    if num % 2 == 0:
        return True

Python also allows you to return more than one value from one return statement. Most other programming languages don't allow this. In most programming languages, you can return only zero or one value from a function. Technically, Python only allows you to return one value, but thanks to Python's syntax, we can make it look like we're returning multiple values:

Code Output
def sum_avg_diff(num1, num2):
    s = num1 + num2
    a = s / 2
    d = num1 - num2
    return s, a, d

print(sum_avg_diff(2, 6))

total, avg, diff = sum_avg_diff(4, 8)
print('The sum of 4 and 8 is:', total)
print('The average of 4 and 8 is:', avg)
print('The difference of 4 and 8 is:', -4)
(8, 4, -4)
The sum of 4 and 8 is: 12
The average of 4 and 8 is: 6
The difference of 4 and 8 is: -4

To return multiple values, separate them with commas (as in the example above). Recall that this is short-hand for creating a tuple, so you're actually returning a tuple from the function. In the examplve above, we also see using tuple notation to extract the three values from the sum_avg_diff function: total, avg, diff = sum_avg_diff(4, 8).

You can actually mix these different return types within the same function:

def is_even(num):
    if num % 2 == 0:
        return True
    
    return False

def middle_value(values):
    if not is_even(len(values)):
        return values[(len(values)-1)//2]
    else:
        return values[(len(values)-1)//2], values[(len(values)+1)//2]

Here, when there are an odd number of elements in values, return the middle value. If there are an even number, return the middle two (since there isn't one value in the exact middle).

There are two problems with the functions we've been writing so far. What do you think they are?

<< Previous Notes Daily Schedule Next Notes >>