CSci 150: Foundations of computer science
Home Syllabus Readings Projects Tests

Creating functions

We've been making liberal use of functions, like int, len, print, and range. Often, though, it's useful to define new functions that aren't built into the language. Why?

Defining a new function is relatively straightforward. Here is an example, which defines a new function named sq for squaring a value:

def sq(x):
    return x ** 2

The def keyword tells Python that we are starting to define a function. This is followed by the name we're giving to our new function, then a set of parentheses listing the names of the arguments that will be passed into the functions. These names are called parameters. This is followed by a colon, and all indented lines below that form the function's body.

Notice a mild distinction between the words argument and parameter. In sq, the name x is a parameter. When we call sq, as in the line “print(3.14 * sq(5))”, the value we include in the parentheses — 5, here — is the argument. The parameter variable (x here) will be set to whatever argument is passed into the function; in this example, a call to “sq(5)” will lead to setting x to 5 before executing the function's body.

The function's body says what to do when the function is called. It is a list of one or more statements. In this case, there is just one statement, a return statement that says that we are done with the function, and the function's value is whatever value appears after return. In the case of sq(5), we return 5², which is 25. Since sq(5) is 25 and 3.14 ⋅ 25 is 78.5, the line “print(3.14 * sq(5))” displays 78.5.

Naturally, functions can call other functions.

def quad(y):
    y2 = sq(y)
    return sq(y2)

For the call quad(3), we'll execute quad's body with y being 3. The first line of quad says to compute sq(y), which translates to calling sq with an argument of 3. The sq function returns 9 in this case, so quad sets y2 to be the value 9. The second line of quad says to compute sq(y2), so we enter sq this time with an argument of 9. That returns 81, and quad says to return this. So quad(3) turns out to be 81.

The body can naturally be more complex.

def find_multiple(multipliernums):
    for n in nums:
        if n % multiplier == 0:
            return n

In this case, find_multiple expects two arguments: The first (named multiplier) should be an integer, while the second (named nums) should be a list of integers.

For example, suppose that a program were to call “find_multiple(5, [23581321345589])”.

  1. Before starting the function call, the computer first configures the multiplier parameter variable as 5 and the nums parameter variable as [23581321345589].

  2. We begin the function's body, in this case entering the loop with n being the first value in nums, which is 2. Since 2 % 5 is 2, we do not enter the if statement.

  3. We go for another iteration, with n being the second value in nums, which is 3. Since 3 % 5 is 3, we do not enter the if statement.

  4. We go for another iteration, with n being the third value in nums, which is 5. Since 5 % 5 is 0, we enter the if statement.

  5. We have now reached a return statement. This says to stop the function immediately; we do not go for any more iterations of the for loop. The function's value is the value following the word return; in this case we find n, whose value is 5, so the value of “find_multiple(5, [23581321345589])” turns out to be 5.

Notice that since the function stopped as soon as it reached a return statement, it never discovered that 55 is also a multiple of 5.

And what if we gave find_multiple a list where it never reaches a return statement, as in “find_multiple(3, [124816])”? Once Python “goes off the end” of a function's body, it simply returns with a special value of None. As it happens, that seems a reasonable value to return for this problem.

However, what if we wanted find_multiple to have a value of 0 if a multiple isn't found in the list? That would be easy enough to do: We'd just add an additional return statement outside the loop.

def find_multiple(multipliernums):
    for n in nums:
        if n % multiplier == 0:
            return n
    return 0

This function has two return statements. But in executing the program, whichever one is reached first will be the only one executed. After all, return says to stop the function's computation immediately. In this case, once a multiple is found, then the first return statement returns that multiple; but if there is no multiple, then it reaches the final return statement and returns 0.