Skip to main content

Foundations of Python Programming: Functions First

Section 4.5 Returning a value from a function

The functions we’ve used as examples thus far have not been all that useful: we enclosed print() functions within our own function, as a way to illustrate parameters, when we could have just used the print() functions on their own. And, the functions we’ve defined so far don’t do much to be useful to help solve a decomposed larger problem. One of the reasons that our illustrative functions don’t appear to be useful is because few of them have returned a value. In this section we will discuss how to create more useful functions that produce useful data for our problem solving.
Functions that don’t return a value are sometimes called procedures, non-fruitful or void functions. Notice how in the function header example below the function definition indicates the function returns -> None, that there is no data being processed, and that there is no return statement. None is a special Python value used to indicate no value at all.

Note 4.5.1.

None is not the same as 0, False, or an empty string (""). It is its own data type (NoneType). Some other computer languages use the term null.
More usefully, a function can also be used to produce a value from from one or more parameters.
You probably can appreciate how a built-in Python function like = abs() will find, and then return, the absolute value of a provided number. Or how = input() returns anything a user types on the keyboard as a string.
These kinds of functions, ones that return values are sometimes called fruitful functions because they produce a result.
How do we write our own fruitful function? Let’s start by creating a very simple mathematical function that we will call square. The square function will take one number as a parameter and return the result of squaring that number. Here is the black-box diagram with the Python code following.
The return statement is followed by an expression which is evaluated. Its result is returned to the caller (the line of code that invoked the function) as the “fruit” of calling this function. Because the return statement can contain any Python expression we could have avoided creating the temporary variable result and simply used return an_integer * an_integer. Try modifying the square function above to see that this works just the same. On the other hand, using temporary variables like result in the function above makes debugging easier, especially when a function is a many step process. These temporary variables are referred to as local variables. In general, programmers should not try to place a multiple step process into the return statement.
Notice something important here: the name of the variable we pass as an argument does not have to have anything to do with the name of the formal parameter — an_integer. You can see this very clearly in the Codelens: it doesn’t matter what names the caller uses — the_integer or another_integer). When square is executed, inside that function, its parameter, a local variable — an_integer, is given the value that is passed, the local temporary variable — result is determined, and finally, is the return value that the function produces.
Return the previous CodeLens example and step through the code once again. This time make note of the Frames being displayed, the appearance (and disappearance of) those variables in those frames, and the values of the variables.
When we suggested earlier that functions could be generalized, this illustrates what we meant. A fruitful function should be coded to deal with any value of parameter(s), process things, and then to provide a value based on its parameter(s). Thus, our square function can work """With any integer...""".
It should also be apparent that using self-documenting variables names, like the_integer and another_integer, make clearer what is being passed to the function and what variables are local to the function (an_integer and result). And, using these kinds of self-documenting variable names makes the code easier to design, develop and debug.
There is one more aspect of fruitful functions. By default, all Python functions return the special value None to indicate there is no value at all. This is not the same as 0, False, nor an empty string "".

Note 4.5.2.

A function will only return a non-None value if it includes an explicit return statement with a value in the function’s body.
Notice in the following how the square function does not contain a return statement. As you step through this example, pay very close attention to the Return Value in the square function’s frame. Then look at what is printed when the function is over.
A problem with this function is that even though it prints the resulting squared value (line 3), that value will not be available to be subsequently displayable, accessed nor used after the function has been executed. Instead, the value None will be returned to the caller’s scope. Since line 6 assigns the function’s return value to square_result, that variable will have None as its value and that is what will be printed in line 7.
Much more usefully one would code a fruitful function that return a value and assigns that value to a variables for later processing or display.
Next, a return statement, once executed, immediately terminates execution of a function, even if it is not the last statement in the function. In the following code, when line 3 executes, the value 5 is returned and assigned to the variable x, then printed. Lines 4 and 5 never execute. Run the following code and try making some modifications of it to make sure you understand why “there” and 10 never print out.
The fact that a return statement immediately ends execution of the code block inside a function is important to understand for writing complex programs. The following example algorithm uses a programming structure, conditionals, that we will cover later, but for now we will use the idea here to hint at what is possible.
Consider a circumstance where a student’s grade has to be greater than or equal to 60% to use it as a prerequisite. A function to check this could be written as follows:
def prerequisite_checker( grade: float ) -> bool:
    """Checks if student meets prerequisite grade requirements"""
    if grade < 60:
        return False
    else:
        return True
Do consider the challenges with using multiple return statements: the programmer (and thus the function’s code) has to repeat the return statement for every circumstance - and cannot overlook even one outcome. In fact, the structured programming tradition considers the use of multiple return statements to be bad practice. Instead, it suggests programmers get into the habit of always having a single return statement as very last line of their function’s body, to avoid ’forgetting’ to include all the returns. This practice, making the last line of every function a return statement, also makes tracing and debugging the complex function code easier.
So far, we have just seen return values being assigned to variables. For example, we had the line square_result = square(the_integer). As with all assignment statements, the right-hand side of that instruction is executed first. It invokes the square function, passing in a parameter value 10 (the current value of the_integer). The function returns a value 100, which completes the evaluation of the right-hand side of the assignment. The value 100 is then assigned to the variable square_result. In this case, the function invocation was the entire expression that was evaluated.
Function invocations, however, can also be used as part of more complicated expressions. For example, square_result = 2 * square(the_integer + 1). In this case, the value 11 is evaluated, passed to the function, and is returned to the right-hand side of the instructions. Then, the returned value (121) is multiplied by 2 to produce the value 242.
To reiterate, when executing the line of code square_result = 2 * square(the_integer + 1), the Python interpreter does these steps:
  1. It’s an assignment statement, so it evaluates the right-hand side expression 2 * square(the_integer + 1).
  2. Looks up the values of the variables square and the_integer: square is a function object and the_integer is 10
  3. Evalutes the argument expression (the_integer + 1) as 11
  4. Pasess 11 as a parameter value to the square function (where an_integer is assigned 11 as a value), get back the return value 121
  5. Substitutes 121 for square, so that the expression now reads 2 * 121
  6. Assigns 242 to variable square_result
Check your understanding

Checkpoint 4.5.3.

    What is wrong with the following function definition:
    def add_em(x, y, z):
        return x+y+z
        print('the answer is', x+y+z)
    
  • You should never use a print statement in a function definition.
  • Although you should not mistake print for return, you may include print statements inside your functions.
  • You should not have any statements in a function after the return statement. Once the function gets to the return statement it will immediately stop executing the function.
  • This is a very common mistake so be sure to watch out for it when you write your code!
  • You must calculate the value of x+y+z before you return it.
  • Python will automatically calculate the value x+y+z and then return it in the statement as it is written
  • A function cannot return a number.
  • Functions can return any legal data, including (but not limited to) numbers, strings, booleans, etc.
  • There is no type hinting.
  • Although type hinting is standard in Python, functions don’t require it.

Checkpoint 4.5.4.

    What will the following function return?
    def add_em(x: int, y: int, z: int):
        print(x+y+z)
    
  • The value None
  • We have accidentally used print where we mean return. Therefore, the function will return the value None by default. This is a VERY COMMON mistake so watch out! This mistake is also particularly difficult to find because when you run the function the output looks the same. It is not until you try to assign its value to a variable that you can notice a difference.
  • The value of x+y+z
  • Careful! This is a very common mistake. Here we have printed the value x+y+z but we have not returned it. To return a value we MUST use the return keyword.
  • The string ’x+y+z’
  • x+y+z calculates a number which represents the sum of the values x, y and z.

Checkpoint 4.5.5.

    What will the following code output?
    def square(x: int) -> int:
        y = x * x
        return y
    
    print(square(5) + square(5))
    
  • 25
  • It squares 5 twice, and adds them together.
  • 50
  • The two return values are added together.
  • 25 + 25
  • The two results are substituted into the expression and then it is evaluated. The returned values are integers in this case, not strings.

Checkpoint 4.5.6.

    What will the following code output?
    def square(x: int) -> int:
        y = x * x
        return y
    
    print(square(square(2)))
    
  • 8
  • It squares 2, yielding the value 4. But that doesn’t mean the next value multiplies 2 and 4.
  • 16
  • It squares 2, yielding the value 4. 4 is then passed as a value to square again, yeilding 16.
  • Error: can’t put a function invocation inside parentheses
  • This is a more complicated expression, but still valid. The expression square(2) is evaluated, and the return value 4 substitutes for square(2) in the expression.

Checkpoint 4.5.7.

    Which will print out first, square, g, or a number?
    def square(x: int) -> int:
        print("square")
        return x*x
    
    def g(y: int) -> int:
        print("g")
        return y + 3
    
    print(square(g(2)))
    
  • square
  • Before executing square, it has to figure out what value to pass in, so g is executed first
  • g
  • g has to be executed and return a value in order to know what paramater value to provide to x.
  • a number
  • square and g both have to execute before the number is printed.

Checkpoint 4.5.8.

8. Write a function named same that takes a string as input, and simply returns that string.

Checkpoint 4.5.9.

9. Write a function called same_thing that returns the parameter, unchanged.

Checkpoint 4.5.10.

10. Write a function called subtract_three that takes an integer or any number as input, and returns that number minus three.

Checkpoint 4.5.11.

11. Write a function called change that takes one number as its input and returns that number, plus 7.

Checkpoint 4.5.12.

12. Write a function named intro that takes a string as input. This string is intended to be a person’s name and the output is a standardized greeting. For example, given the string “Becky” as input, the function should return: “Hello, my name is Becky and I love COMP 1701.”

Checkpoint 4.5.13.

13. Write a function called s_change that takes one string as input and returns that string, concatenated with the string “for fun.”.
You have attempted of activities on this page.