Parameters, return values, and scope

Some functions need to have information in order to do their jobs. Some functions compute some value that they need to make available once they’re finished. And many functions need to create variables that are used only temporarily, while the function is being executed.

Passing parameters to functions

One way to get information into a function is through parameters. We have already seen several examples of calling functions that take parameters. Here’s one:

The sqrt function requires one parameter, a float, that it will use to compute the function’s return value. That is, the sqrt function gets some of the information it needs to do its job—namely, the number of which it is to compute the square root—through a parameter.

Here’s another function we’ve seen that takes parameters:

set_fill_color(1.0, 0.0, 0.0)   # set the current fill color to red

The set_fill_color function requires three parameters: the fractions of red, green, and blue in the fill color you would like future drawing commands to use.

The functions you have defined so far have taken no parameters. They did not need any information in order to do their jobs. Now let’s see how to define functions that take parameters.

You put the parameters between the parentheses in the function’s header. Each parameter is actually a special variable. We call a parameter that appears in a function’s header a formal parameter. If a function takes more than one formal parameter, we separate them by commas in the function’s header.

Here is an example of a program that contains a function to draw the outline of a square:

The formal parameters are variables named x, y, and s; we expect them to be assigned int values. When draw_square is called, the first value in the function call, 100, is copied into the first formal parameter in the function header, x. The second value in the function call, 120, is copied into the second formal parameter in the function header, y. The third value in the function call, 50, is copied into the third formal parameter, s.

After Python has copied these three values into the formal parameters, it sets the program counter to the first line of the body of draw_square. At this point, there are three parameters available: x, y, and s. These parameters work just like any other variables. We can use them to compute expressions. If we want to, we can even change their values—because once the function is called, formal parameters work just like any other variables.

We call the numbers 100, 120, 50 that are actually passed into the function actual parameters, sometimes also called arguments. When a function is called, the values of the actual parameters are computed and copied in order into the formal parameters. The actual parameters are values (or expressions) in the function call instruction, and the formal parameters are the variables used in the function itself. An actual parameter can be a complicated expression. What matters is the value that it evaluates to.

In summary: A formal parameter is a variable that is initialized with a copy of an actual parameter at the point of call. The values of the actual parameters are copied into the corresponding formal parameters, position by position, left to right.

Exercise: robot square function

Objective: Write and make use of a simple function that takes a parameter.

Here is a program that causes a robot to drive in a square. However, the square is always the same size. Write a function that takes a single parameter indicating the length of the sides of the square, and call that function with appropriate parameters to cause the robot to drive in a square of size 2.

Exercise: smiley function

Objective: Write and make use of a simple function that takes multiple parameters.

Write a function that draws a smiley face. The function should take three parameters that indicate the location and size of the smiley face. Call the function to draw a smiley face with a radius of 25 centered at 100, 150 on the screen. The size should be specified in pixels; you will need to compute and make use of a scaling factor based on that size.

Some functions return values

Expressions compute values and make those values available for further use. Functions can also compute values and make those values available for further use. They don’t have to do that, but they can. For example, the sqrt function returns a value, which is the square root of the value of the actual parameter passed to it. When a function computes a value that is available for further use, we say that the function returns the value.

Anywhere a value is needed, a call of a function that returns the right type of value (for example, an int, a float, a string, or a boolean) can appear.

For example, in a print statement, we can print the value returned by a function call:

Let’s dissect what is meant by “Anywhere a value is needed, a call of a function that returns the right type of value can appear.” Consider this line of code:

print( 8 + 12 )

Of course, this code outputs 20. Now, if we were to call int(8.48528137423857), the value returned by the call of int would be 8, so let’s change the code to

print( int(8.48528137423857) + 12 )

Do you see what I’ve done here? We needed an int value as the left operand of +. Instead of 8, we used int(8.48528137423857): a call to the int function, which returns the value 8. Why did I choose int(8.48528137423857)? Because I’m going to go a step further. The sqrt function, when passed the actual parameter 72, returns the float value 8.48528137423857. So we can change the code to

print( int(sqrt(72)) + 12 )

We needed the value 8.48528137423857, and instead we used the function call sqrt(72). Functions are called in the order that their values are needed to compute the expression. So in this example, sqrt(72) calls the sqrt function with the actual parameter 72. The result 8.48528137423857 is then made available. Then the result of calling int is needed, so the int function is called, passing in the value 8.48528137423857 as a parameter. int executes and returns the value 8, which is made available. Finally, 8 + 12 is computed.

Built-in functions: len, int, float, str

len returns the length of a string, as an int value.

As we’ve seen, int converts from some other type to an int. If the number is a float, any part of the number to the right of the decimal point is truncated (dropped). If the number is too large to store in an int, Python returns a long int instead.

If you try to convert from a string that contains characters that represent something other than a number, int will fail and your program will terminate. print(int("123")) will work. print( int("buffalo")) will not.

float and str convert to float and string types, respectively, and work as you might expect.

Functions that return random values

We can call the function randint from the random module to get (nearly) random values. For example:

This line prints a random integer between 5 and 20, inclusive. In other words, it’s equally likely to print any integer in the range from 5 to 20.

What if you want a random floating-point number? Call uniform, also from the random module:

Exercise: coin flip

Objective: Make use of different results of a function call to take different actions.

Write a loop that simulates flipping a coin 5 times, and prints out “heads” or “tails” after each flip. Use the Python randint function to determine if the outcome of each flip should be heads or tails.

Exercise: lots of smiles

Objective: Write a function that makes use of a different function you’ve written many times.

Write a function that draws n smiley faces of random size at random locations on the screen, where n is a parameter to the function. Click on the left arrow next to the draw function to unfold that function in the editor, and call your function to draw 20 random smiley faces. You may assume that the screen width and height are both 200. As a starting point, here is a function to draw a single smile. Don’t forget needed import statements.

Defining your own functions with return values

When the value returned by a function is needed, the function is called. The current value of the program counter is saved. Then the program counter is set to the first line of the function. Python executes the body of the function. The function returns and the program counter is set to its value before the function call when one of two things happens:

Executing a return-statement does two things:

  1. return returns the value following return (if any) to the calling code.
  2. return immediately stops execution of the body of the function and resumes execution at the point of call. That is, the program counter goes back to just after the function was called.

Here’s a really simple example.

As we now know, anywhere a value is needed, you can substitute an expression or function call that returns a value. For example, you could do something like this:

The line print( "I computed the value!" ) is not printed on the screen, since the return-statement before the print-statement will always immediately give control back to the calling function by setting the value of the program counter. In fact, Python will warn you that you have done something silly. Lines of code that cannot be reached are called dead code. Normally, you should not include dead code in a program.

Exercise: area 51

Objective: Write a function that returns a value.

First, write a function circle_area51 that computes the area of a circle of radius 51, and returns that area. Call the function and print the result to verify that it works.

Then write a function circle_area that takes a parameter radius, and computes the area o a circle with that radius. Call the function three times, to compute the areas of circles of size 3, 5, and 51, and print the results.

Local and global variables

Variables in Python are either local to a single function or global and accessible by any function. Python has some particular rules that determine whether a variable is local or global and how you access the variable.

Local variables

When the first time you assign to a variable is inside a function, that variable is a local variable. A local variable is not accessible by any code outside the function in which it is defined. A local variables exists during that call of the function, and then it ceases to exist. You can think of local variables as disposable—use them in the function, and throw them away.

In this example, the variable x is local to some_function. The line print( x ) within some_function prints 4.

But the line print( x ) after the call to some_function is an error. That’s because the first time that x is assigned is within some_function, and so x is local to some_function. Therefore, x is not known outside some_function.

Local variables:

  1. cannot be accessed outside of the function where they are created.
  2. are destroyed as the function returns.
  3. are used to store temporary computation results.

Formal parameters are local variables, too.

We say that a local variable is in scope inside the function where it is created, after assignment.

Local variables are good. Most lines of code compute something, and store it in a nicely named local variable. Later lines of code use that variable to compute something else, and store it in a nice local. Using well-named local variables for intermediate computation is as important as commenting to make code understandable and modifiable by human readers.

Function frames and scope

The values of variables are stored in memory. Where? Each function call creates an area of memory called a frame to store values of the local variables that it creates. When the function is exited, the frame is destroyed, and the local variables are no longer in scope. This is good: the function is cleaning up after itself.

Global variables

Every now and then, you want a variable to be available to many functions. Such a variable cannot be local, because a local variable is accessible only within the function in which it’s first assigned to.

A variable that is accessible by many functions is a global variable. Useful though may be, global variables also have their seamy underbelly, and so you should use them only when necessary.

Global variables are initialized outside of function definitions. If you are simply using the value of a global variable, you can access that global anywhere in any line of code (within the same file) that runs after the initialization of the variable.

Changing the value of a global variable is a big deal. Why? Doing so may affect countless other functions, some of which you might not even know about or have written. Where possible, you should avoid changing global variables, and Python forces you to explicitly tell it that the variable is global before using any assignment statements.

In any function where you want to assign to a global variable, you must use the global keyword to indicate that you really do intend to change the value of the global variable. Here’s an example.

Here, the function print_x accesses the global variable x. Because x has the value 5 when the function is called, the assignment statement within the function assigns the value 6 to x, and both print statements print the value 6.

It’s important to remember that the keyword global is not needed to create a global variable. If you just intend to make use of the value, you should not use the keyword global, since the keyword makes it possible to change the value, and this could affect any code that depends on the global variable.

What’s wrong with global variables? There are two obvious ways to get values into a function. The first is to use pass actual parameters into formal parameters; the second is to set a global variable that the function can use. If you choose the second, then

  1. It is not obvious from reading the function header what values the function requires and makes use of.
  2. If you change the value of the global for some other reason that has nothing to do with that function, the function will behave differently, and that’s hard to predict or debug, since you may have forgotten all about this function in the meantime.
  3. You will add more variable names to the global frame. There is a single global frame used to store the values of global variable values; you need to find a unique variable name for each new global variable.

Although we’ll use some global variables for demonstration purposes, and there are even a few occasionally good uses of global variables, using a global variable should always make you very uncomfortable at the least. Frequently, you can wrap bare code inside a function, making variables local, and then pass the values of those variables into whichever function needs them.

Stylistically, using local variables and parameters to transfer data makes the flow of information clear throughout the program. Some function you want to use take some parameters; you think about what values you need and how to compute them. The function returns that value and you know you can use it. If the function uses or changes global values to get information in or out, it’s harder to see how to connect that function to other code.

Global variables can serve as named constants

There is one use of global variables that is fairly safe and, in fact, good programming practice: as named constants. For example, a chemistry program might use Avogadro’s number in several functions. If you typed the actual number in every time you used it in an equation, you might make a mistake, and it might be hard for a reader to figure out the equations, if they don’t recognize the number. A global variable can store the number, make it easy to change that number without searching and replacing, and makes the code more readable.

AVOGADRO = 6.0221415e23

We call global variables used to store values that don’t change constants. It’s good programming practice to type constants using all uppercase letters, so that they are recognizable as constants.

There are some built-in global variables in Python modules that you can use by importing them.

Notice that pi is not capitalized in this case. The capitalization is just a convention, and apparently the Python designers didn’t use that convention. Bad Guido! (That’s Guido van Rossum, the designer of Python, who is also known as The Benevolent Dictator For Life.)

Although a global variable such as pi imported from the math library is intended to be constant, Python doesn’t prevent you from changing it.

(I checked on the Internet about this story that the Great State of Kansas attempted to change the value of π to 3. Turns out it’s not true. Yay, Kansas!)

With great power comes great responsibility. Python lets you change the value of variables intended to serve as named constants. Don’t do it.

A style note about boolean values and expressions

Consider the following code:

The test x == 5 evaluates to a boolean expression, as must any test in an if-statement. This boolean expression must have either the value True or False, right? And if the boolean expression x == 5 has the value True, the function returns True. If the boolean expression x == 5 has the value False, the function returns False.

In other words, the function returns exactly the same value that the expression x == 5 evaluates to. So here’s another way to write this function, which is shorter and more direct:

How does it work? We evaluate the expression x == 5, which comes to either True or False, and we return exactly this boolean value. Notice that in either version of is_five, we return True precisely when x equals 5, and we return False precisely when x does not equal 5.

You should avoid code like the first version of the function. When you write code the first way, with an if-else-statement that just mirrors the value of a boolean expression, you are declaring to all the world “I don’t understand the boolean type!!!”