Program Arcade GamesWith Python And Pygame

 < Previous Home Next >

Chapter 9: Functions

9.1 Introduction to Functions

Functions are used for two reasons. First, they make code easier to read and understand. Second, they allow code to be used more than once.

By the end of this chapter, we will learn to create our own functions so that we can write programs that say drawSnowman(50, 100) to draw a snowman where we want.

Imagine a set of code that controls a toy car. Suppose the programmer wishes to make the car move forward. To do this, the programmer executes the following commands:

turnOnMotor()
pauseOneSecond()
turnOffMotor()


By defining a function, the program can improve readability and reuse code multiple times:

def moveForward():
turnOnMotor()
pauseOneSecond()
turnOffMotor()


By itself, this code will not cause the car to move forward. It does tell the computer how to do moveForward. You have to call the function to actually run the code in the function and get the car to move forward:

moveForward()


With a whole library of functions defining what the car can do, a final program might look like:

moveForward()
turnLeft()
moveForward()
moveForward()
turnRight()
moveForward()


Remember that moveForward has three lines of code. Each one of these other commands has multiple lines of code. By using functions, we can repeat the commands without repeating all the code contained within, making for a much smaller program.

Function names are very important. If the function names are descriptive, even a non-programmer should be able to read a set of code and get an idea what is happening. Function names follow the same rules as variable names, and should start with a lower case letter.

9.2 Function Parameters

Functions can take parameters. These can be used to increase the flexibility of a function by altering what it does based on parameters passed to it. For example, our function called moveForward() drives the robot foward for one second. But the function could be changed to take a parameter that specifies how many seconds to move forward. For example moveForward(3) would move forward for three seconds and moveForward(6) would move for six seconds.

Adjusting the function for the robot might look like:

def moveForward(time):
turnOnMotor()
for i in range(time):
pauseOneSecond()
turnOffMotor()


Here is a different function that can be run without a robot car. This function will calculate and print out the volume of a sphere:

def volumeSphere(radius):
pi = 3.141592653589
volume = (4 / 3) * pi * radius ** 3
print("The volume is", volume)

Parameters are assigned values when a function is called, not when it is defined.

The name of the function is volumeSphere. The data going into the functions will be stored in a new variable called radius. The resulting volume is printed to the screen. The radius variable does not get a value here. Frequently new programmers get confused because parameter variables aren't given a value when the function is defined, so it doesn't look legal. Parameters are given a value when the function is called.

To call this function, use:

volumeSphere(22)


The radius variable in the function is created and initialized with a value of 22. The function's code is run once the execution reaches the call to the function.

What if we need to pass in more than one value? Multiple parameters can be passed to a function, each parameter separated by a comma:

def volumeCylinder(radius, height):
pi = 3.141592653589
volume = pi * radius ** 2 * height
print("The volume is", volume)


That function may be called by:

volumeCylinder(12, 3)


Parameters are done in order, so radius will get the 12, and height will get the 3 value.

Unfortunately, these example functions are limited. Why? If a person wanted to use the volumeCylinder function to calculate the volume in a six-pack, it wouldn't work. It only prints out the volume of one cylinder. It is not possible to use the function's result for one cylinder's volume in an equation and multiply it by six to get a six-pack volume.

This can be solved by using a return statement. For example:

def volumeCylinder(radius, height):
pi = 3.141592653589
volume = pi * radius ** 2 * height
return volume


Return is not a function, and does not use parentheses. Don't do return(volume).

Because of the return, this function could be used later on as part of an equation to calculate the volume of a six-pack like this:

sixPackVolume = volumeCylinder(2.5, 5) * 6


The value returned from volumeCylinder goes into the equation and is multiplied by six.

There is a big difference between a function that prints a value and a function that returns a value. Look at the code below and try it out.

# Function that prints the result
def sumPrint(a, b):
result = a + b
print(result)

# Function that returns the results
def sumReturn(a, b):
result = a+b
return result

# This prints the sum of 4+4
sumPrint(4, 4)

# This does not
sumReturn(4, 4)

# This will not set x1 to the sum
# It actually gets a value of 'None'
x1 = sumPrint(4, 4)

# This will
x2 = sumReturn(4, 4)


9.3 Documenting Functions

Functions in Python typically have a comment as the first statement of the function. This comment is delimited using three double quotes, and is called a docstring. A function may look like:

def volumeCylinder(radius, height):
"""Returns volume of a cylinder given radius, height."""
pi = 3.141592653589
volume = pi * radius ** 2 * height
return volume


The great thing about using docstrings in functions is that the comment can be pulled out and put into a website documenting your code using a tool like Sphinx. Most languages have similar tools that can help make documenting your code a breeze. This can save a lot of time as you start working on larger programs.

9.4 Variable Scope

The use of functions introduces the concept of scope. Scope is where in the code a variable is “alive” and can be accessed. For example, look at the code below:

# Define a simple function that sets
# x equal to 22
def f():
x = 22

# Call the function
f()
# This fails, x only exists in f()
print (x)


The last line will generate an error because x only exists inside of the f() function. The variable is created when f() is called and the memory it uses is freed as soon as f() finishes.

Here's where it gets complicated.

A more confusing rule is accessing variables created outside of the f() function. In the following code, x is created before the f() function, and thus can be read from inside the f() function.

# Create the x variable and set to 44
x = 44

# Define a simple function that prints x
def f():
print(x)

# Call the function
f()


Variables created ahead of a function may be read inside of the function only if the function does not change the value. This code, very similar to the code above, will fail. The computer will claim it doesn't know what x is.

# Create the x variable and set to 44
x=44

# Define a simple function that prints x
def f():
x += 1
print(x)

# Call the function
f()


Other languages have more complex rules around the creation of variables and scope than Python does. Because Python is straight-forward it is a good introductory language.

9.5 Pass-by-copy

Functions pass their values by creating a copy of the original. For example:

# Define a simple function that prints x
def f(x):
x += 1
print(x)

# Set y
y = 10
# Call the function
f(y)
# Print y to see if it changed
print(y)


The value of y does not change, even though the f() function increases the value passed to it. Each of the variables listed as a parameter in a function is a brand new variable. The value of that variable is copied from where it is called.

This is reasonably straight forward in the prior example. Where it gets confusing is if both the code that calls the function and the function itself have variables named the same. The code below is identical to the prior listing, but rather than use y it uses x.

# Define a simple function that prints x
def f(x):
x += 1
print(x)

# Set x
x = 10
# Call the function
f(x)
# Print x to see if it changed
print(x)


The output is the same as the program that uses y. Even though both the function and the surrounding code use x for a variable name, there are actually two different variables. There is the variable x that exists inside of the function, and a different variable x that exists outside the function.

9.6 Functions Calling Functions

It is entirely possible for a function to call another function. For example, say the functions like the following were defined:

def armOut(whichArm, palmUpOrDown):
# code would go here

def handGrab(hand, arm):
# code goes here


Then another function could be created that calls the other functions:

def macarena():
armOut("right", "down")
armOut("left", "down")
armOut("right", "up")
armOut("left", "up")
handGrab("right", "left arm")
handGrab("left", "right arm")
# etc


9.7 Main Functions and Globals

Global variables are pure evil.

As programs get large it is important to keep the code organized and put into functions. Python allows us to write code at “indent level 0.” This describes most of the code we've written so far. Our code is lined up on the left and not contained in functions.

This philosophy is like heaping your clothes on the middle of your floor, or keeping all your tools in a pile on the workbench. It only works well when you don't have much stuff. Even when you don't have much stuff it is still messy.

All your code and all your variables should be placed in functions. This will keep your code organized. It will also help when you need to track down a bug in the program. Variables created at “indent level 0” are called global variables. Global variables are a very bad thing. Why? Because any piece of code anywhere can change their value. If you have a 50,000 line program, each line of code can change that global variable. If instead you keep the variable in a function, then only that code in the function can change the variable. Thus, if you have an unexpected value in a variable, you only need to look through the maybe 50 lines of code in your function. Otherwise you have to check every line of code in your entire program!

A better way to write a program in Python would be to follow this pattern:

def main():
print("Hello world.")

main()


In this case all the code that I normally would have run at indent level 0 is placed in the main function. The last line of the file calls main.

But wait! There's another problem we need to fix. In Chapter 15, we will talk about how to break our program into multiple files. We can use the import command to bring in functions from other modules we created. If we used the import command on this module, it would automatically start running the main function. We don't want that. We want the program that imports it to control when the function is called.

To fix this problem we can have our program check a global variable defined automatically by Python. (I know, I just said global variables were bad, right?) That variable is called __name__, with two underscores before and after it. We can check it to see if this code is being imported or run. If the code is being run, Python will automatically set the value of that variable to __main__. By using an if statement we will only call the main function if the code is being run. Otherwise the code will just define the main function. The code that imported it can call the function when desired.

This is how all your Python code should be run:

def main():
print("Hello world.")

if __name__ == "__main__":
main()


One of the reasons I love Python as an first language is that you aren't required to use this complexity until you need it. Other languages, like Java, require it no matter how small your program.

To make things easier in this book we do not show our examples using this pattern. But after this book your programs will likely be complex enough that it will make life easier if you don't “throw all your clothes in a pile” so to speak.

If you are super-enthused about programming, try writing your programs starting this way now. While it may be a bit more challenging to begin with, it will make writing programs easier later on. It is also a good way to learn about how to properly manage your data and its scope.

Here is an example that shows how to do the base PyGame template using this pattern:

Using this template is not required if I'm the one teaching the course. I'm OK during your first semester if you pile your clothes in the middle of the floor. I'm just happy you are wearing clothes. (For the neat-freaks, we can clean this program up even more when we get to the chapter on “classes.”)