Programming in Python · Lesson 9
Functions
Built-in, imported, and programmer-defined functions; void functions, parameters and arguments, return values, default parameters, and variable scope in Python.
Lesson purpose
Up to now every program has run from top to bottom as one block of code. This lesson introduces functions: named blocks of code that perform one task and can be called from anywhere in a program. Students learn the three kinds of functions, how a function call changes the flow of execution, how to write void functions, how to pass data in through parameters, how to send data back with return (including multiple return values and default parameter values), and how scope decides which variables a function can see and change.
For students who know C# and JavaScript: a Python function is closest to a JavaScript function declaration. There are no return types or parameter types in the header, no braces (the indented block is the body), and the keyword is def. Unlike C#, Python has no method overloading.
Learning objectives
By the end of the lesson, students should be able to:
- Differentiate built-in, imported, and programmer-defined functions.
- Explain how functions support modular programming and list the advantages of using functions.
- Explain the purpose of a function call and trace the flow of execution into and out of a function.
- Describe the characteristics of a void function and name functions using the same conventions as variables.
- Identify the function declaration, function name, and function body, and identify functions and function calls in code and pseudocode.
- Explain function parameters and arguments, how arguments correspond to parameters, and trace the handoff between them.
- Explain return values, use an assignment statement to collect a return value, and trace a function that returns a value.
- Return two or more values from a function and unpack them into variables.
- Identify the parts of a function signature and use default parameter values.
- Explain scope, differentiate global and local variables, use the
globalkeyword, and describe pass by value and pass by reference as the book presents them.
1. Function basics
What is a function?
A function is a named block of program code that performs a specific task. Functions fall into three categories:
| Category | Where it comes from | Examples |
|---|---|---|
| Built-in functions | Always available; no import needed | print(), input(), len(), abs() |
| Imported functions | Packaged in modules; must be added with import |
random.randint(), math.sqrt() |
| Programmer-defined functions | Written by you with def |
welcome(), correct() |
This lesson focuses on programmer-defined functions.
Why write functions? The trivia example
The book uses a trivia-night program. Without functions, every question repeats the same checking logic:
score = 0
answer = input("Lake Chapala is the largest freshwater lake in which country? ")
if answer == "Mexico":
print("Correct!\nYou earn 1 point.")
score += 1
else:
print("That is not correct.")
answer = input("Mac Gargan is the alter ego of what Spider-Man villain? ")
if answer == "Scorpion":
print("Correct!\nYou earn 1 point.")
score += 1
else:
print("That is not correct.")
Apart from the answers "Mexico" and "Scorpion", the if-else logic is identical. Removing that repetition is exactly what functions are for.
Flow of execution
A function call activates a function. When Python reaches a call, the flow of execution jumps into the function, runs its statements, then returns to the statement after the call in the main program. The same function can be called from many places.
Main program Function
statement
statement
function call ───────► statement
statement
statement ◄─────── statement
statement
function call ───────► (runs again)
Advantages of functions
- Modularize code by grouping the statements that perform each task.
- Simplify modifications, because a change usually touches only one function instead of statements scattered through the program.
- Reduce the amount of code by reusing one block many times.
- Encapsulate code, which makes debugging easier.
2. Void functions
Declaring and calling a void function
A void function performs a task (such as displaying a message) but sends nothing back to the main program. The keyword def tells Python that a function definition follows.
def welcome():
print("Welcome to Trivia!")
print("No fair looking online.")
welcome()
print()
Output:
Welcome to Trivia!
No fair looking online.
Parts of a function
| Part | In the example | Meaning |
|---|---|---|
| Function declaration (function header) | def welcome(): |
One line that names the function and lists its parameters; ends with a colon |
| Function name | welcome |
Follows the same naming rules and conventions as variables (lowercase, words joined by underscores) |
| Function body | the two indented print() lines |
The block of statements that defines what the function does |
| Function call | welcome() |
Runs the function; the parentheses are required |
In Python, the function definition must come before the code that calls it, so the interpreter already knows the function when it reaches the call. A common layout is: imports, then function definitions, then the main program.
C#comparison:static void Welcome() { ... }becomesdef welcome():plus an indented body. There is novoidkeyword; a function is “void” simply because it has noreturnvalue.
Spotting functions in pseudocode
Look for behavior that is repeated on different data but works the same way each time:
Ask the first trivia question and obtain an answer.
Compare the inputted answer to the real answer and display an appropriate message.
Increase the score by 1 if the answer is correct.
Ask the second trivia question and obtain an answer.
Compare the inputted answer to the real answer and display an appropriate message.
Increase the score by 1 if the answer is correct.
The compare-and-score steps are the same for every question; only the “real answer” changes. That is a function waiting to be written. Pseudocode for a function often reads like “apply this behavior to each item.”
3. Functions with parameters
Parameters
Function parameters are variables listed inside the parentheses of the function declaration. They are a template for the data the function can accept.
def correct(answer_is_correct):
if answer_is_correct:
print("Correct!")
print("You earn 1 point.")
else:
print("That is not correct.")
correct() is still a void function: it takes data in but sends nothing back.
Arguments
A function argument is the data passed to a function in the function call.
correct(False)
Output:
That is not correct.
The handoff
When the function is called, each argument is copied into the matching parameter: the call correct(False) binds the value False to the parameter answer_is_correct. Arguments are matched to parameters left to right by position.
A function can take several parameters, and a call can pass variables, literals, or a mix:
def receipt(size, toppings, price):
print("Your order:")
print(str(size) + "-inch", toppings, "pizza.")
print("Total ${:0.2f}".format(price))
print("Enjoy Trivia night!")
selection = "Veggie"
receipt(12, selection, 12.99)
Output:
Your order:
12-inch Veggie pizza.
Total $12.99
Enjoy Trivia night!
Good practice: name parameters for their purpose, just like variables.
Using correct() in the trivia program
def correct(answer_is_correct):
if answer_is_correct:
print("Correct!")
print("Each correct answer is worth 1 point.")
else:
print("Not correct.")
score = 0
answer = input("Lake Chapala is the largest freshwater lake in which country? ")
is_correct = answer == "Mexico"
correct(is_correct)
if is_correct:
score += 1
The Boolean stored in is_correct is used twice: once as the argument and once to update the score. The next section removes that duplication by having the function return a value.
4. Return values
The return statement
A return value is data a function sends back to the code that called it, using a return statement. Think of it as asking someone (the function) to work something out and come back (return) with the answer.
def correct(answer_is_correct):
if answer_is_correct:
print("Correct!")
print("Each correct answer is worth 1 point.")
return 1
else:
print("Not correct.")
return 0
score = 0
answer = input("Lake Chapala is the largest freshwater lake in which country? ")
score += correct(answer == "Mexico")
answer = input("Mac Gargan is the alter ego of what Spider-Man villain? ")
score += correct(answer == "Scorpion")
Tracing the call
- The user types
Mexico, which is stored inanswer. score += correct(answer == "Mexico")callscorrect().- The call passes one argument: the Boolean result of
answer == "Mexico". - The function has one parameter,
answer_is_correct, which receivesTrue. - Because the parameter is
True, the function returns1. - The returned
1replaces the call expression, so the statement becomesscore += 1.
This is the core advantage of functions: a call that returns a value can be treated as if it were that value.
Two parameters: showing the right answer
def correct(user_guess, correct_answer):
if user_guess == correct_answer:
print("Correct!")
print("Each correct answer is worth 1 point.")
return 1
else:
print("Not correct.")
print("The correct answer was", correct_answer + ".")
return 0
score = 0
answer = input("Lake Chapala is the largest freshwater lake in which country? ")
score += correct(answer, "Mexico")
answer = input("Mac Gargan is the alter ego of what Spider-Man villain? ")
score += correct(answer, "Scorpion")
print("Final score:", score)
To add another question, copy the two lines and change only the question text and the correct answer.
Every function returns something
In Python, a function with no return statement (or a bare return) returns the special value None. If you see None where you expected a value, the function is probably missing a return.
def f():
pass
print(f()) # None
5. Multiple return values
A function can return several values by separating them with commas. The caller assigns them to the same number of variables, in the same order:
return 5, 6
return price, tax, tip, total
first, second = returns_two_values()
Pizza-price example (small cheese $7.99, small veggie $10.99, any other small $12.99; medium adds $2, large adds $6):
def get_pizza_price(pizza_type):
if pizza_type == "Cheese":
small_price = 7.99
elif pizza_type == "Veggie":
small_price = 10.99
else:
small_price = 12.99
medium_price = small_price + 2
large_price = small_price + 6
return small_price, medium_price, large_price
small, medium, large = get_pizza_price("Veggie")
print("Small: ${:.2f}".format(small))
print("Medium: ${:.2f}".format(medium))
print("Large: ${:.2f}".format(large))
Output:
Small: $10.99
Medium: $12.99
Large: $16.99
Behind the scenes:
return a, b, cactually returns one tuple,(a, b, c), andsmall, medium, large = ...unpacks it.C#programmers can think of a tuple return like(double, double, double); JavaScript programmers can compare it to returning an array and destructuring it.
6. Function signatures and default values
The function signature
The function signature is what uniquely defines a function for the interpreter. It includes:
- the keyword
def - the function name
- the function parameters (the parameter list)
In def get_pizza_price(pizza_type):, the name is get_pizza_price and the parameter list is (pizza_type).
Clarifying note: the book says you “can’t have two functions with the same name.” Python will not stop you, but a second
defwith the same name silently replaces the first, even if the parameter lists differ. Python has no overloading likeC#, so in practice every function needs its own name.
Default parameter values
A default value is written with = in the parameter list. It is used only when the call does not supply that argument.
def get_pizza_price(pizza_type, medium_increase=2, large_increase=6):
if pizza_type == "Cheese":
small_price = 7.99
elif pizza_type == "Veggie":
small_price = 10.99
else:
small_price = 12.99
medium_price = small_price + medium_increase
large_price = small_price + large_increase
return small_price, medium_price, large_price
small, medium, large = get_pizza_price("Veggie") # uses defaults 2 and 6
small, medium, large = get_pizza_price("Veggie", 1, 2) # on sale: overrides both
Results: 10.99, 12.99, 16.99 with defaults, and 10.99, 11.99, 12.99 with the sale arguments.
Rules:
- Arguments are still matched to parameters left to right.
- Parameters without defaults must come first; parameters with defaults follow.
This works like optional parameters in C# (int mediumIncrease = 2) and default parameters in JavaScript.
7. Scope
What is scope?
Scope is the visibility of program components, such as variables, to other parts of the program. Consider:
number = 5
def example(number):
print(number)
example(12)
print(number)
Output is 12, then 5. The parameter number is a different variable from the number outside the function.
Local and global variables
A local variable is created inside a function and is visible only within that function. This code causes an error:
def example2():
number = 5
example2()
print(number) # NameError: name 'number' is not defined
A global variable is defined outside any function and is accessible throughout the program; functions can read it without any extra work.
external = 1
def example():
internal = 2
print(internal)
print(external)
example()
print(external)
print(internal) # NameError: name 'internal' is not defined
Rules and best practices from the book:
- Define global variables at the beginning of the program, before functions and before the main code.
- Some programmers prefix globals with
gorg_to show they are global. - Variables assigned inside a function are automatically local; no extra keyword is needed.
- Avoid global variables when you can; they undermine modularization and make programs harder to modify and debug.
Same name, different scopes
Two variables can share a name if they have different scopes (one global and one local, or locals in different functions). When Python looks up a name inside a function, it checks the function’s own local names first (parameters are local names too), then the global names.
Clarifying note: the book ranks “local variables, then parameters, then global variables.” Parameters are simply local variables that get their starting value from the call, so the practical rule is local first, then global (then built-in names like
Changing a global inside a function
By default, assigning to a name inside a function creates a new local variable, so the change to the global does not stick:
external = 1
def example():
internal = 2
print("Internal inside function:", internal)
external = 5
print("External inside function:", external)
example()
print("External outside function:", external)
Output:
Internal inside function: 2
External inside function: 5
External outside function: 1
The keyword global, followed by only the variable name, tells Python the name refers to the global variable:
external = 1
def example():
internal = 2
print("Internal inside function:", internal)
global external
external = 5
print("External inside function:", external)
example()
print("External outside function:", external) # now prints 5
Extra caution: because any assignment makes a name local for the whole function, reading a global and then assigning to it in the same function fails.
print(external)followed later byexternal = 5raisesUnboundLocalError. Useglobal(or better, a parameter and a return value).
Pass by value (as the book presents it)
def add_one(number):
print("Inside function, before addition:", number)
number += 1
print("Inside function, after addition:", number)
number = 5
print("Outside function, before function call:", number)
add_one(number)
print("Outside function, after function call:", number)
Output:
Outside function, before function call: 5
Inside function, before addition: 5
Inside function, after addition: 6
Outside function, after function call: 5
The book calls this pass by value: the function gets a copy of the data, so the original variable keeps its value. To change number in the main program, return the new value and assign it:
number = add_one(number) # add_one() must end with: return number
Avoid using the same identifiers for arguments and parameters; it makes the code harder to read.
Pass by reference (as the book presents it)
def add_one_to_first_number(number_list):
print("Inside function, before addition:", number_list)
number_list[0] += 1
print("Inside function, after addition:", number_list)
number_list = [10, 5, 4, 2, 6]
print("Outside function, before function call:", number_list)
add_one_to_first_number(number_list)
print("Outside function, after function call:", number_list)
Output:
Outside function, before function call: [10, 5, 4, 2, 6]
Inside function, before addition: [10, 5, 4, 2, 6]
Inside function, after addition: [11, 5, 4, 2, 6]
Outside function, after function call: [11, 5, 4, 2, 6]
The book calls this pass by reference: the function receives a reference to the original data, so changes to the list’s contents persist after the function ends. Lists and user-defined objects behave this way. It saves memory and time, but the risk of accidentally changing data outweighs those savings on modern computers, so reserve it for situations where the algorithm clearly needs it.
Clarifying note: Python technically uses one rule for every argument, often called pass by object reference. The parameter always refers to the same object as the argument. What differs is the object: integers and strings are immutable, so
number += 1makes the parameter point to a new object and the caller’s variable is untouched. Lists are mutable, sonumber_list[0] += 1changes the shared object. But assigning a whole new list to the parameter (number_list = [0]) does not affect the caller. ForC#students: this matches passing a reference type without therefkeyword. JavaScript behaves the same way with arrays and objects.
Classroom application
- Trace the flow. Give students a short program with two function calls and have them number the order in which each line runs.
- Refactor the trivia game. Start from the repeated if-else version and rewrite it with a
correct(user_guess, correct_answer)function that returns 1 or 0; add a third question. - Parameter handoff drill. Show calls such as
receipt(14, "Pepperoni", 15.5)and have students write which value lands in each parameter before running it. - Multiple returns. Write
min_max(numbers)that returns the smallest and largest values of a list and unpack them in the caller. - Defaults. Write
greet(name, greeting="Hello")and call it with one and two arguments. - Scope puzzles. Predict the output of the
number = 5/example(12)program and the global/local examples, then run them to check. - Mutable vs. immutable arguments. Write one function that adds 1 to an integer parameter and one that appends to a list parameter; compare what the caller sees.
Common misconceptions
- “Defining a function runs it.”
defonly creates the function; it runs when called with parentheses. - “
welcomeandwelcome()are the same.” Without parentheses you refer to the function object; nothing runs. - “Printing inside a function returns the value.”
print()shows a value; onlyreturnsends it back to the caller. - “A function without
returnreturns nothing at all.” It returnsNone. - “Code after
returnstill runs.”returnends the function immediately. - “The parameter and the argument must have the same name.” Arguments are matched by position, not by name.
- “Defining a second function with the same name is an overload.” It replaces the first definition.
- “Parameters with default values can come first.” Parameters without defaults must come before those with defaults.
- “Assigning to a global inside a function changes it.” Without
global, the assignment creates a local variable. - “Changing a parameter always changes the caller’s variable.” Rebinding a parameter never does; mutating a shared list does.
- “A list default like
bucket=[]starts fresh each call.” The default list is created once and shared by every call; useNoneas the default and create the list inside the function.
Lesson summary
- A function is a named block of code that performs a specific task.
- Built-in functions are always available; imported functions come from modules and require
import; programmer-defined functions are created withdef. - Functions modularize code, reduce repeated code, simplify changes, and make debugging easier by encapsulating code in discrete groups.
- A function declaration includes the function name and parameters; the function body holds the statements, which may include a
returnstatement. - A function call transfers the flow of execution to the function and back again; a call can pass arguments, which are matched left to right to the function’s parameters.
- A void function performs a task without sending data back; functions send data back with
return, and a function without one returnsNone. - A function can return multiple values separated by commas; the caller unpacks them into variables in the same order.
- The function signature is the keyword
def, the function name, and the parameter list; default values make parameters optional. - Scope is the visibility of variables. Local variables are visible only inside their function; global variables are visible throughout the program, and
globallets a function change one. - The book describes integers as passed by value (the caller’s variable is unchanged) and lists as passed by reference (changes to contents persist); in Python this comes from immutable versus mutable objects.
Course Notes