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

More on creating functions

Variable scope

Consider this little program.

import math

def sq(x):
    return x ** 2

def f(x):
    sinx2 = sq(math.sin(x))
    x2 = sq(x)
    return sinx2 / x2


Here is the order in which this program executes.

  1. We enter f with x being 0.1.
  2. The f function first computes sin 0.1 ≈ 0.0998, then passes that to sq.
    1. We enter sq with x being 0.0998.
    2. The sq function returns the square of that, 0.009967.
    The f function saves this result, 0.009967, into sinx2.
  3. Then f function passes x to sq. The question is… what is x now? In entering sq, we just set x to be 0.0998. However, that was sq's x; f has its own x, which has never changed from 0.1. Since f is passing its x, the number 0.1 is passed to x.
    1. We enter sq with x being 0.1.
    2. The sq function returns the square of that, 0.01.
    The f function saves this result, 0.01, into x2.
  4. Finally, f computes the ratio of sinx2 and x2, 0.9967, and returns that result.

The important point here is that each function has its own “world” of variables. When sq stashes something into one of its variables, it has no effect on variables in f's world, even if the variables happen to share the same name.

Global variables

There is an exception to this: Variables that exist outside any functions are available to all functions. These are known as global variables, for obvious reasons. For example:

pow = 5

def sq(x):
    return x ** pow

Here, the sq function refers to pow. This variable doesn't exist within its own “world,” so Python looks to the global variables and finds that pow is 5.

However, if sq had any code that wrote to pow (i.e., “pow = y”), Python would conclude that sq has its own pow variable, separate from the global pow. In practice, this is rarely an issue. (But if you wanted sq to actually change the global pow, you could do that by inserting a special statement “global pow” into the sq function.)

In practice, expert programmers disdain global variables, except for circumstances where the global variable is a constant: That is, where the variable's value never changes from its initial value, in which case the variable is just an alternative name for that value.

Functions returning nothing

Sometimes we want functions that don't return anything. Such functions exist purely for their side effects: Usually, the function displays something. The built-in print function is a significant example.

For example, suppose we want a function that displays a muliplication table for the numbers 1 through the parameter value.

def print_table(n):
    for i in range(1n + 1):
        line = ''
        for j in range(1i + 1):
            line = line + ' {0:3d}'.format(i * j)

print('multiplication to 3')
print('multiplication to 5')

This function lacks any return statement. When the program calls “print_table(3)”, the function displays the multiplication table and then reaches the function body's end. At that point, it would return, and the calling code would go on to the next line, “print('multiplication to 5')”.

Suppose somebody were to write “x = print_table(3)” for some reason. Since print_table ends without returning anything, x would get a useless value (namely, None). In practice, nobody would ever try to use the value from a function that doesn't return anything.

By the way, one sometimes wants a return statement that exits the function without having the function return anything. This is easy enough: Just write “return” with nothing following it.

def show_sum(pow):
    total = 0
    while True:
        line = input()
        if line == '':
        total += float(line) ** pow

In this case, when the function is called, the function reads several lines from the user, adding the values on the lines. But when the user finally enters an empty line, the function displays the current value of total, and then the function ends its computation.