8. Creating Functions
In the last chapter we learned how to create variables and use
them in expressions. In this chapter we are going to build on
that knowledge to create functions. Functions are groups of commands we
give to the computer.
We’ve already used functions, for example the print
function and the draw_line
function. Now we are going to learn to make our own functions.
Functions help us by:
Making our code easier to read. By writing our own functions we can have functions to
draw_tree
anddraw_horse
which is a lot easier to read than long blocks of code that just draw lines and rectangles.Abstracting our code. By using variables, we can abstract our code and tell a function where to draw a tree, how big to draw it, and what color to draw it. This is a lot more flexible than one set of code that can only draw a tree in one exact place and one exact size.
Re-using our code. By calling a function, I can re-use code over and over. If it takes ten lines of code to draw a horse, I can put that in a function. Then if I want to draw three horses, I don’t need thirty lines of code to do it.
Dividing a complex problem into simpler ones. The key to programming, and a lot of things in life, is to learn how to take a large, overwhelmingly complex problem, and divide it into small problems. For example, we have functions that can display dots on the screen. Then there functions that can draw a line using those dots. Then functions that can draw triangles using those lines. Then functions that can draw trees using those triangles. Then finally a forest function that uses the trees function. Drawing a complex forest is broken down into simple functions that build off each other.
Making code easier to maintain. If there is a problem in your program, it is easier to find when your program is divided into parts. If your car isn’t moving correctly, and there is a
move_car
function, that’s easy to find. Plus, if all cars move using that function, I can fix a problem for every car in my game in one spot.
8.1. Creating Simple Functions
Defining a function in Python is easy. Here’s an example:
1 2 3 | def print_hello(): """ This is a comment that describes the function. """ print("Hello!") |
To write a function:
Start with the keyword
def
, which is short for “define.”Next, give the function a name. Function names follow the same rules that variable names follow. They must:
Start with a lower case letter. (Or in special cases, an underscore.)
After the first letter, only use letters, numbers, and underscores.
Spaces are not allowed. Use underscores instead.
While upper-case letters can be used, function names are normally all lower-case.
After that, we have a set of parenthesis. Inside the parenthesis will go parameters. We’ll explain those in a bit.
Next, a colon.
All the code that goes in the function will be on the following lines. That code must be indented four spaces. The first line that is not indented, signifies the function is done.
Usually we start a function with a multi-line comment that explains what the function does.
Note
Function definitions go below the import
statements, and above the
rest of the program. While you can put them somewhere else, you shouldn’t.
Defining a function doesn’t cause the computer to do anything. It is like giving a recipe to the computer. Give someone a recipe for banana bread and they know how to make it. They haven’t actually made it yet, they just know how. You have to tell them to make banana bread. That is, after we define the function we must call the function before the code in it runs.
To call a function, type the function name and follow it by parenthesis.
Do not use def
, that is only used when we define what the function does,
not when we tell the computer to run it.
Below is a program that defines print_hello
, then
calls it twice.
1 2 3 4 5 6 7 | def print_hello(): """ This is a comment that describes the function. """ print("Hello!") # This calls our function twice. print_hello() print_hello() |
You can define and use multiple functions. But all function definitions should go before the main code. In this example, see how we’ve defined two functions and then we call them in our main code.
1 2 3 4 5 6 7 8 9 10 11 | def print_hello(): print("Hello!") def print_goodbye(): print("Bye!") # Here is the main code, after all the function # definitions. print_hello() print_goodbye() |
8.2. Creating a Main Function
Actually, almost all code should go in a function. It is a good practice
to put the main starting point of your program in a function called main
and call it.
In this next example, we do just that. See how we took the two function
calls and put them in a main
function. Also, note that we call main
at the end. Without that, our program won’t do anything at all.
main
function.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def print_hello(): print("Hello!") def print_goodbye(): print("Bye!") def main(): """ This is my main program function """ print_hello() print_goodbye() # Call (run) the main function main() |
Remember how our drawing code always starts with import arcade
? That
just imports a lot of functions that are defined in the Arcade library
so we can use those functions ourselves. It doesn’t run anything, just
imports functions.
Eventually we’ll learn to write our own modules we import. Right now, we couldn’t do that with our code because attempting to import the code would actually run it. What we want is simply to make the functions available for us to use.
With that in mind, a better habit to get into is to check if we are
trying to import
the file before calling the main
function.
The statement, in this next example, looks a little weird.
In fact, it is weird enough I just look it up and copy/paste it any
time I want to use it. Don’t worry about understanding how it works yet,
just realize it is a good practice and why.
main
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def print_hello(): print("Hello!") def print_goodbye(): print("Bye!") def main(): print_hello() print_goodbye() # Only run the main function if we are running this file. Don't run it # if we are importing this file. if __name__ == "__main__": main() |
If this is too confusing, just call the main()
method instead. Eventually
we’ll learn about imports and if
statements. Then it will make more sense.
8.3. Take in Data with Parameters
Functions are even more powerful if we can feed data into them to process.
Here is a simple example that will take in a number and print it. Notice how
I’ve created a new variable my_number
in between the parenthesis.
This is called a function parameter. It is just a variable that
is given a value equal to what is passed in when called.
In the example below, it
is given first a 55
, then 25
, and finally an 8
.
1 2 3 4 5 6 7 | def print_number(my_number): print(my_number) print_number(55) print_number(25) print_number(8) |
You can pass in multiple numbers, just separate them with a comma. In
this case a
will be 11 and b
will be 7.
1 2 3 4 5 | def add_numbers(a, b): print(a + b) add_numbers(11, 7) |
Occasionally, new programmers want to set the parameter values inside the function. This is wrong. If you do this, the function would only work for those values. The power is in specifying the numbers outside the function. We don’t want the function to be limited to only certain data values.
This example works, but it is wrong. Because if we ever changed line 8 to pass in different values, the program would ignore those values because on lines 3 and 4 they are always over written with 11 and 7.
1 2 3 4 5 6 7 8 | # This is wrong def add_numbers(a, b): a = 11 b = 7 print(a + b) add_numbers(11, 7) |
8.4. Returning and Capturing Values
Functions can not only take in values, functions can return values.
8.4.1. Returning values
For example, here is a function that returns two numbers added together.
See how it uses a return
statement on line four to return the result out of the
function.
1 2 3 4 | # Add two numbers and return the results def sum_two_numbers(a, b): result = a + b return result |
Note
Return is not a function, and does not use parentheses. Don’t do
return(result)
.
This only gets us half-way there. Because if we call the function now, not much happens. Take a look at this example:
# This doesn't do much, because we don't capture the result
sum_two_numbers(22, 15)
The numbers get added. They get returned to us. But we do nothing with the result. So if you run this program, nothing prints to the screen and we can’t tell that anything happened.
8.4.2. Capturing Returned Values
We need to capture the result. We do that by setting a variable equal to
the value the function returned. In this next code example we capture the result by
putting a variable to the left of the function call and using the =
(assignment)
operator. Later the code prints the value, but that’s not capturing! That is
just using the result we captured and printing it.
# Capture the function's result into a variable
# by putting "my_result =" in front of it.
# (Use whatever variable name best describes the data,
# don't blindly use 'my_result' for everything.)
my_result = sum_two_numbers(22, 15) # <--- This line CAPTURES the return value
# Now that I captured the result, print it.
print(my_result) # <--- This is printing, NOT capturing.
Now the result isn’t lost. It is stored in the my_result
variable
which we can print or use some other way.
We will do this a lot, so pause for a second and set it sit in your mind. I’ll wait.
Great, now imagine you have an instructor or co-worker watching over your shoulder. You have a function. It returns a value. The person over your shoulder says, “Ok, now you just have to capture the value.” The pressure is on.
Don’t panic! Remember to capture
the value returned from a function, put a variable to the left of it and
use an =
operator.
8.4.3. Volume Cylinder Example
Let’s use what we learned for something more practical. Here is a function that returns the volume of a cylinder. Notice how we take in two parameters and return the result of the calculation.
1 2 3 4 | def volume_cylinder(radius, height): pi = 3.141592653589 volume = pi * radius ** 2 * height 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:
six_pack_volume = volume_cylinder(2.5, 5) * 6
See how the value returned from volume_cylinder
goes into the equation and is
multiplied by six. We would not be able to chain together calculations like
that if all we could do was print
, instead of return
.
There is a big difference between a function that prints a value and a function that returns a value. Look at the code below. Pause for a bit and make sure you can follow how it works. Read the comments. This is a common source of confusion for new programmers, so spend a bit of time studying it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # This function will print the result def sum_print(a, b): result = a + b print(result) # This function will return the result def sum_return(a, b): result = a + b return result # This code prints the sum of 4+4, because the function has a print sum_print(4, 4) # This code prints nothing, because the function returns, and doesn't print sum_return(4, 4) # This code will not set x1 to the sum. # The sum_print function does not have a return statement, so it returns # nothing! # x1 actually gets a value of 'None' because nothing was returned x1 = sum_print(4, 4) print("x1 =", x1) # This will set x2 to the sum and print it properly. x2 = sum_return(4, 4) print("x2 =", x2) |
This next code example has a very common issue new programmers face. It is not unusual to get stuck looking at code like this example below. Your task: how would you print the result of the function? See if you can figure it out without looking ahead.
def calculate_average(a, b):
""" Calculate an average of two numbers """
result = (a + b) / 2
return result
# Pretend you have some code here
x = 45
y = 56
# Wait, how do I print the result of this?
calculate_average(x, y)
What’s the answer?
How do we print the result of calculate_average
?
In the code above, the program can’t print
the result because the result
variable only exists inside the function.
Instead, use
a new variable outside the function, and the =
operator
to capture the result:
def calculate_average(a, b):
""" Calculate an average of two numbers """
result = (a + b) / 2
return result
# Pretend you have some code here
x = 45
y = 56
average = calculate_average(x, y)
print(average)
8.5. 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. In this case, the docstring is on line 2:
1 2 3 4 5 | def volume_cylinder(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. That can be part of a larger website that documents your code. For example, here is the documentation for drawing a circle, generated automatically from the function comment. In fact, everything on that page is automatically generated. Most languages have similar tools that can help make documenting a breeze. This can save a lot of time as you start working on larger programs.
8.6. 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. Every heard the phrase “What happens in Vegas, stays in Vegas?”
Well, variables created in a function stay in the 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 how to access 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()
8.7. Pass-by-Copy
When you call a function and set a value to a function parameter, that value is a copy of the original. For example take a look at this code:
# 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.
8.8. Functions Calling Functions
For each of the examples below, think about what would print. Check to see if you are right. If you didn’t guess correctly, spend to the time to understand why.
8.8.1. Example 1
In this example, note that if you don’t use a function, it doesn’t run.
# Example 1
def a():
print("A")
def b():
print("B")
def c():
print("C")
a()
8.8.2. Example 2
# Example 2
def a():
b()
print("A")
def b():
c()
print("B")
def c():
print("C")
a()
8.8.3. Example 3
# Example 3
def a():
print("A")
b()
def b():
print("B")
c()
def c():
print("C")
a()
8.8.4. Example 4
# Example 4
def a():
print("A start")
b()
print("A end")
def b():
print("B start")
c()
print("B end")
def c():
print("C start and end")
a()
8.8.5. Example 5
# Example 5
def a(x):
print("A start, x =", x)
b(x + 1)
print("A end, x =", x)
def b(x):
print("B start, x =", x)
c(x + 1)
print("B end, x =", x)
def c(x):
print("C start and end, x =", x)
a(5)
8.8.6. Example 6
While line 3 of this example increases x
, the x
variable in the function is a different variable than the
x
that is in the rest of the program. So that x
never changes.
# Example 6
def a(x):
x = x + 1
x = 3
a(x)
print(x)
8.8.7. Example 7
This example is similar to the prior example, but we return x
at the end. Turns out, it doesn’t matter. Because we
never do anything with the return value. So the global variable x
still doesn’t increase. See the next example.
# Example 7
def a(x):
x = x + 1
return x
x = 3
a(x)
print(x)
8.8.8. Example 8
This example take the value returned from a
and stores it back into x
. How? By doing x = a(x)
instead of
just a(x)
.
# Example 8
def a(x):
x = x + 1
return x
x = 3
x = a(x)
print(x)
8.8.9. Example 9
# Example 9
def a(x, y):
x = x + 1
y = y + 1
print(x, y)
x = 10
y = 20
a(y, x)
8.8.10. Example 10
While you can have two return
statements in a function, once you hit the first return
the function ends. In
this case, return y
never runs, because we already returned from the function in the prior line.
# Example 10
def a(x, y):
x = x + 1
y = y + 1
return x
return y
x = 10
y = 20
z = a(x, y)
print(z)
8.8.11. Example 11
This is not something you can do in every programming language. You can return two values by using a comma and listing them.
# Example 11
def a(x, y):
x = x + 1
y = y + 1
return x, y
x = 10
y = 20
z = a(x, y)
print(z)
8.8.12. Example 12
If you return two values out of a function, you can capture them this way.
# Example 12
def a(x, y):
x = x + 1
y = y + 1
return x, y
x = 10
y = 20
x2, y2 = a(x, y) # Most computer languages don't support this
print(x2)
print(y2)
8.8.13. Example 13
# Example 13
def a(my_data):
print("function a, my_data = ", my_data)
my_data = 20
print("function a, my_data = ", my_data)
my_data = 10
print("global scope, my_data =", my_data)
a(my_data)
print("global scope, my_data =", my_data)
8.8.14. Example 14
We will talk more about these next two examples when we talk about “lists” and “classes” later. These examples don’t operate like you might expect at first. Take a look and see what is different. We’ll explain why it works differently later.
# Example 14
def a(my_list):
print("function a, list = ", my_list)
my_list = [10, 20, 30]
print("function a, list = ", my_list)
my_list = [5, 2, 4]
print("global scope, list =", my_list)
a(my_list)
print("global scope, list =", my_list)
8.8.15. Example 15
# Example 15
# New concept!
# Covered in more detail in a later chapter
def a(my_list):
print("function a, list = ", my_list)
my_list[0] = 1000
print("function a, list = ", my_list)
my_list = [5, 2, 4]
print("global scope, list =", my_list)
a(my_list)
print("global scope, list =", my_list)
8.9. Review
This chapter was all about functions.
We learned how to define functions using the def
keyword.
We can call functions just by using the function name followed by parenthesis.
Functions can take in data by using parameters.
Functions return data using the return
statement.
We can capture that data by setting a variable equal to the function call.
Functions can be documented with comments.
Variables created inside a function cannot be accessed outside the function.
Parameter variables contain copies of the original data and can be modified without
changing the original.
Functions can call other functions, which can call yet more functions.