4 Functions
This post is a modified version of material from Automate the Boring Stuff with Python by Al Sweigart.
Interactive Books with AI tutor:
- Ask about anything. Highlight any sentence and click Ask AI — answers are grounded in this chapter.
- Run the code inline. Edit code block. Hit Run to execute it here.
- Submit exercises for review. Write your solution and click Submit — the tutor reviews correctness and you can ask follow-up questions.

A function is like a mini program within a program. Python provides several built-in functions, such as the print(), input(), and len() functions from the previous chapters, but you can also write your own. In this chapter, you'll create functions, explore the call stack used to determine the order in which functions in a program run, and learn about the scope of variables inside and outside functions.
Creating Functions
To better understand how functions work, let's create one. Enter this program into the file editor and save it as helloFunc.py:
def hello():
# Prints three greetings
print('Good morning!')
print('Good afternoon!')
print('Good evening!')
hello()
hello()
print('ONE MORE TIME!')
hello()The first line is a def statement, which defines a function named hello(). The code in the block that follows the def statement is the body of the function. This code executes when the function is called, not when the function is first defined.
The hello() lines after the function are function calls. In code, a function call is just the function's name followed by parentheses, possibly with some number of arguments in between the parentheses. When the program execution reaches these calls, it will jump to the first line in the function and begin executing the code there. When it reaches the end of the function, the execution returns to the line that called the function and continues moving through the code as before.
Because this program calls hello() three times, the code in the hello() function is executed three times. When you run this program, the output looks like this:
Good morning!
Good afternoon!
Good evening!
Good morning!
Good afternoon!
Good evening!
ONE MORE TIME!
Good morning!
Good afternoon!
Good evening!A major purpose of functions is to group code that gets executed multiple times. Without a function defined, you would have to copy and paste this code each time you wanted to run it, and the program would look like this:
print('Good morning!')
print('Good afternoon!')
print('Good evening!')
print('Good morning!')
print('Good afternoon!')
print('Good evening!')
print('ONE MORE TIME!')
print('Good morning!')
print('Good afternoon!')
print('Good evening!')In general, you always want to avoid duplicating code, because if you ever decide to update the code (for example, because you find a bug you need to fix), you'll have to remember to change the code in every place you copied it.
As you gain programming experience, you'll often find yourself deduplicating, which means getting rid of copied-and-pasted code. Deduplication makes your programs shorter, easier to read, and easier to update.
Your Turn! Try running the code below — then experiment by adding a new greeting or calling hello() a few more times!
Python Playground
Python Playground
Edit the code and click Run to see what happens.
Run the code first so AI Tutor can see the latest output or error.
Arguments and Parameters
When you call the print() or len() function, you pass it values, called arguments, by entering them between the parentheses. You can also define your own functions that accept arguments. Enter this example into the file editor and save it as helloFunc2.py:
def say_hello_to(name):
# Prints three greetings to the name provided
print('Good morning, ' + name)
print('Good afternoon, ' + name)
print('Good evening, ' + name)
say_hello_to('Alice')
say_hello_to('Bob')When you run this program, the output looks like this:
Good morning, Alice
Good afternoon, Alice
Good evening, Alice
Good morning, Bob
Good afternoon, Bob
Good evening, BobThe definition of the say_hello_to() function has a parameter called name. Parameters are variables that contain arguments. When a function is called with arguments, the arguments are stored in the parameters. The first time the say_hello_to() function is called, it's passed the argument 'Alice'. The program execution enters the function, and the parameter name is automatically set to 'Alice', which gets printed by the print() statement. You should use parameters in your function if you need it to follow slightly different instructions depending on the values you pass to the function call.
One special thing to note about parameters is that the value stored in a parameter is forgotten when the function returns. For example, if you added print(name) after say_hello_to('Bob') in the previous program, the program would give you an error because there is no variable named name. This variable gets destroyed after the function call say_hello_to('Bob') returns, so print(name) would refer to a name variable that does not exist.
The terms define, call, pass, argument, and parameter can be confusing. To review their meanings, consider a code example:
def say_hello_to(name):
# Prints three greetings to the name provided
print('Good morning, ' + name)
print('Good afternoon, ' + name)
print('Good evening, ' + name)
say_hello_to('Al')To define a function is to create it, just as an assignment statement like spam = 42 creates the spam variable. The def statement defines the say_hello_to() function. The say_hello_to('Al') line calls the now-created function, sending the execution to the top of the function's code. This function call is passing the string 'Al' to the function. A value being passed in a function call is an argument. Arguments are assigned to local variables called parameters. The argument 'Al' is assigned to the name parameter.
It's easy to mix up these terms, but keeping them straight will ensure that you know precisely what the text in this chapter means.
Return Values and return Statements
When you call the len() function and pass it an argument such as 'Hello', the function call evaluates to the integer value 5, which is the length of the string you passed it. In general, the value to which a function call evaluates is called the return value of the function.
When creating a function using the def statement, you can specify the return value with a return statement, which consists of the following:
- The
returnkeyword - The value or expression that the function should return
In the case of an expression, the return value is whatever this expression evaluates to. For example, the following program defines a function that returns a different string depending on the number it is passed as an argument. Enter this code into the file editor and save it as magic8Ball.py:
import random
def get_answer(answer_number):
# Returns a fortune answer based on what int answer_number is, 1 to 9
if answer_number == 1:
return 'It is certain'
elif answer_number == 2:
return 'It is decidedly so'
elif answer_number == 3:
return 'Yes'
elif answer_number == 4:
return 'Reply hazy try again'
elif answer_number == 5:
return 'Ask again later'
elif answer_number == 6:
return 'Concentrate and ask again'
elif answer_number == 7:
return 'My reply is no'
elif answer_number == 8:
return 'Outlook not so good'
elif answer_number == 9:
return 'Very doubtful'
print('Ask a yes or no question:')
input('> ')
r = random.randint(1, 9)
fortune = get_answer(r)
print(fortune)When the program starts, Python first imports the random module. Then comes the definition of the get_answer() function. Because the function isn't being called, the code inside it is not run. Next, the program calls the random.randint() function with two arguments: 1 and 9. This function evaluates a random integer between 1 and 9 (including 1 and 9 themselves), then stores it in a variable named r.
Now the program calls the get_answer() function with r as the argument. The program execution moves to the top of that function, storing the value r in a parameter named answer_number. Then, depending on the value in answer_number, the function returns one of many possible string values. The execution returns to the line at the bottom of the program that originally called get_answer() and assigns the returned string to a variable named fortune, which then gets passed to a print() call and printed to the screen.
Note that because you can pass return values as arguments to other function calls, you could shorten these three lines
r = random.randint(1, 9)
fortune = get_answer(r)
print(fortune)to this single equivalent line:
print(get_answer(random.randint(1, 9)))Remember that expressions consist of values and operators; you can use a function call in an expression because the call evaluates to its return value.
Your Turn! Run the Magic 8-Ball below — try asking it a question and see what fortune you get!
Python Playground
Magic 8-Ball
Edit the code and click Run to see what happens.
Run the code first so AI Tutor can see the latest output or error.
The None Value
In Python, a value called None represents the absence of a value. The None value is the only value of the NoneType data type. (Other programming languages might call this value null, nil, or undefined.) Just like the Boolean True and False values, you must always write None with a capital N.
This value-without-a-value can be helpful when you need to store something that shouldn't be confused for a real value in a variable. One place where None is used is as the return value of print(). The print() function displays text on the screen, and doesn't need to return anything in the same way len() or input() does. But since all function calls need to evaluate to a return value, print() returns None. To see this in action, enter the following into the interactive shell:
>>> spam = print('Hello!')
Hello!
>>> None == spam
TrueBehind the scenes, Python adds return None to the end of any function definition with no return statement. This behavior resembles the way in which a while or for loop implicitly ends with a continue statement.
You can add named parameters to the functions you write as well, but first, you'll have to learn about the list and dictionary data types in Chapters 6 and 7. For now, just know that some functions have optional named parameters you can specify when calling the function.
The Call Stack
Imagine that you had a meandering conversation with someone. You talked about your friend Alice, which then reminded you of a story about your co-worker Bob, but first you had to explain something about your cousin Carol. You finished your story about Carol and went back to talking about Bob, and when you finished your story about Bob, you went back to talking about Alice. But then you were reminded about your brother David, so you told a story about him, and then you got back to finishing your original story about Alice. Your conversation followed a stack-like structure, like in Figure 4-1. In a stack, items get added or removed from the top only, and the current topic is always at the top of the stack.

Like your meandering conversation, calling a function doesn't send the execution on a one-way trip to the top of a function. Python will remember which line of code called the function so that the execution can return there when it encounters a return statement. If that original function called other functions, the execution would return to those function calls first, before returning from the original function call. The function call at the top of the stack is the execution's current location.
Open a file editor window and enter the following code, saving it as abcdCallStack.py:
def a():
print('a() starts')
b()
d()
print('a() returns')
def b():
print('b() starts')
c()
print('b() returns')
def c():
print('c() starts')
print('c() returns')
def d():
print('d() starts')
print('d() returns')
a()If you run this program, the output will look like this:
a() starts
b() starts
c() starts
c() returns
b() returns
d() starts
d() returns
a() returnsWhen a() is called, it calls b(), which in turn calls c(). The c() function doesn't call anything; it just displays c() starts and c() returns before returning to the line in b() that called it. Once the execution returns to the code in b() that called c(), it returns to the line in a() that called b(). The execution continues to the next line in the a() function, which is a call to d(). Like the c() function, the d() function also doesn't call anything. It just displays d() starts and d() returns before returning to the line in a() that called it. Because d() contains no other code, the execution returns to the line in a() that called d(). The last line in a() displays a() returns before returning to the original a() call at the end of the program.
The call stack is how Python remembers where to return the execution after each function call. The call stack isn't stored in a variable in your program; rather, it's a section of your computer's memory that Python handles automatically behind the scenes. When your program calls a function, Python creates a frame object on the top of the call stack. Frame objects store the line number of the original function call so that Python can remember where to return. If the program makes another function call, Python adds another frame object above the other one on the call stack.
When a function call returns, Python removes a frame object from the top of the stack and moves the execution to the line number stored in it. Note that frame objects always get added and removed from the top of the stack, and not from any other place. Figure 4-2 illustrates the state of the call stack in abcdCallStack.py as each function is called and returns.

The top of the call stack is the currently executing function. When the call stack is empty, the execution is on a line outside all functions.
The call stack is a technical detail that you don't strictly need to know about to write programs. It's enough to understand that function calls return to the line number they were called from. However, understanding call stacks makes it easier to understand local and global scopes, described in the next section.
🎯 Your Turn! Run the call stack example below — watch the order of the output and see if you can trace which function is on top of the stack at each step!
Python Playground
Call Stack Explorer
Edit the code and click Run to see what happens.
Run the code first so AI Tutor can see the latest output or error.
Local and Global Scopes
Only code within a called function can access the parameters and variables assigned in that function. These variables are said to exist in that function's local scope. By contrast, code anywhere in a program can access variables that are assigned outside all functions. These variables are said to exist in the global scope. A variable that exists in a local scope is called a local variable, while a variable that exists in a global scope is called a global variable. A variable must be one or the other; it cannot be both local and global.
Think of a scope as a container for variables. There is only one global scope, created when your program begins. When your program terminates, it destroys the global scope, and all of its variables get forgotten. A new local scope gets created whenever a program calls a function. Any variables assigned in the function exist within the function's local scope. When the function returns, the local scope gets destroyed, along with these variables.
Python uses scoping because it enables a function to modify its variables, yet interact with the rest of the program through its parameters and its return value only. This narrows down the number of lines of code that might be causing a bug. If your program contained nothing but global variables, and contained a bug caused by a variable set to a bad value, you might struggle to track down the location of this bad value. It could have been set from anywhere in the program, which could be hundreds or thousands of lines long! But if the bug occurred in a local variable, you can restrict your search to a single function.
For this reason, while using global variables in small programs is fine, it's a bad habit to rely on global variables as your programs get larger and larger.
Scope Rules
When working with local and global variables, keep the following rules in mind:
- Code that is in the global scope, outside all functions, can't use local variables.
- Code that is in one function's local scope can't use variables in any other local scope.
- Code in a local scope can access global variables.
- You can use the same name for different variables if they are in different scopes. That is, there can be a local variable named
spamand a global variable also namedspam.
Let's review these rules with examples.
Code That Is in the Global Scope Can't Use Local Variables
Consider the following code, which will cause an error when you run it:
def spam():
eggs = 'sss'
spam()
print(eggs)The program's output will look like this:
Traceback (most recent call last):
File "C:/test1.py", line 4, in <module>
print(eggs)
NameError: name 'eggs' is not definedThe error happens because the eggs variable exists only in the local scope created when spam() is called. Once the program execution returns from spam(), that local scope gets destroyed, and there is no longer a variable named eggs. So when your program tries to run print(eggs), Python gives you an error saying that eggs is not defined. This makes sense if you think about it; when the program execution is in the global scope, no local scopes exist, so there can't be any local variables. This is why you can only reference global variables in the global scope.
Code That Is in a Local Scope Can't Use Variables in Other Local Scopes
Python creates a new local scope whenever a program calls a function, even when the function is called from another function. Consider this program:
def spam():
eggs = 'SPAMSPAM'
bacon()
print(eggs) # Prints 'SPAMSPAM'
def bacon():
ham = 'hamham'
eggs = 'BACONBACON'
spam()When the program starts, it calls the spam() function, creating a local scope. The spam() function sets the local variable eggs to 'SPAMSPAM', then calls the bacon() function, creating a second local scope. Multiple local scopes can exist at the same time. In this new local scope, the local variable ham gets set to 'hamham', and a local variable eggs (which differs from the one in spam()'s local scope) gets created and set to 'BACONBACON'. At this point, the program has two local variables named eggs that exist simultaneously: one that is local to spam() and one that is local to bacon().
When bacon() returns, Python destroys the local scope for that call, including its eggs variable. The program execution continues in the spam() function, printing the value of eggs. Because the local scope for the call to spam() still exists, the only eggs variable is the spam() function's eggs variable, which was set to 'SPAMSPAM'. This is what the program prints.
Code That Is in a Local Scope Can Use Global Variables
So far, I've demonstrated that code in the global scope can't access variables in a local scope; nor can code in a different local scope. Now consider the following program:
def spam():
print(eggs) # Prints 'GLOBALGLOBAL'
eggs = 'GLOBALGLOBAL'
spam()
print(eggs)Because the spam() function has no parameter named eggs, nor any code that assigns eggs a value, Python considers the function's use of eggs a reference to the global variable eggs. This is why the program prints 'GLOBALGLOBAL' when it's run.
Local and Global Variables Can Have the Same Name
Technically, it's perfectly acceptable to use the same variable name for a global variable and local variables in different scopes. But, to simplify your life, avoid doing this. To see what could happen, enter the following code into the file editor and save it as localGlobalSameName.py:
def spam():
eggs = 'spam local'
print(eggs) # Prints 'spam local'
def bacon():
eggs = 'bacon local'
print(eggs) # Prints 'bacon local'
spam()
print(eggs) # Prints 'bacon local'
eggs = 'global'
bacon()
print(eggs) # Prints 'global'When you run this program, it outputs a sequence demonstrating how each scope's eggs variable is independent.
# Draw lines with decreasing length:
for i in range(7, 1, -1):
print('-' * (i * i))
time.sleep(0.1)You can modify the 9 and the 7 values in the two for loops if you want to change how wide the spike becomes. The rest of the code will continue to work just fine with these new values.
Summary
Functions are the primary way to compartmentalize your code into logical groups. Since the variables in functions exist in their own local scopes, the code in one function cannot directly affect the values of local variables in other functions. This limits the sections of code able to change the values of your variables, which can be helpful when it comes to debugging.
Functions are a great tool to help you organize your code. You can think of them as black boxes: they have inputs in the form of parameters and outputs in the form of return values, and the code in them doesn't affect variables in other functions.
In previous chapters, a single error could cause your programs to crash. In this chapter, you learned about try and except statements, which can run code when an error has been detected. This can make your programs more resilient to common error cases.
Practice Questions
Short Answer
Question 1 of 15
Why are functions advantageous to have in your programs?
Hint
Think about code reuse and deduplication.
Practice Programs
For practice, write programs to do the following tasks.
The Collatz Sequence
Write a function named collatz() that has one parameter named number. If number is even, then collatz() should print number // 2 and return this value. If number is odd, then collatz() should print and return 3 * number + 1.
Then, write a program that lets the user enter an integer and that keeps calling collatz() on that number until the function returns the value 1. (Amazingly enough, this sequence actually works for any integer; sooner or later, using this sequence, you'll arrive at 1! Even mathematicians aren't sure why. Your program is exploring what's called the Collatz sequence, sometimes called "the simplest impossible math problem.")
Remember to convert the return value from input() to an integer with the int() function; otherwise, it will be a string value. To make the output more compact, the print() calls that print the numbers should have a sep=' ' named parameter to print all values on one line.
The output of this program could look something like this:
Enter number:
3
3 10 5 16 8 4 2 1Hint: An integer number is even if number % 2 == 0, and it's odd if number % 2 == 1.
Python Challenge
The Collatz Sequence
Write a function collatz(number) that prints and returns number // 2 if number is even, or 3 * number + 1 if number is odd. Then read an integer from the user and keep calling collatz() until it returns 1. Use sep=' ' for compact output.
Run the code first so AI Tutor can see the latest output or error.
Hints
Reference Solution
Input Validation
Add try and except statements to the previous project to detect whether the user entered a non-integer string. Normally, the int() function will raise a ValueError error if it is passed a non-integer string, as in int('puppy'). In the except clause, print a message to the user saying they must enter an integer.
Python Challenge
Input Validation for Collatz
Extend the Collatz program with try/except so that it prints a friendly message if the user enters a non-integer string.
Run the code first so AI Tutor can see the latest output or error.
Hints
Reference Solution
Check out other books by Al Sweigart, free online (https://inventwithpython.com) or available for purchase.
This content is adapted from from Automate the Boring Stuff with Python by Al Sweigart. The original work is licensed under Creative Commons license. This version is also made available under the same terms.