Python functions — stop repeating code once and for all
Python functions are the tool that transforms messy code into organised, reusable code. Until now every program you wrote was a sequence of instructions that ran from top to bottom, once. Functions let you name a block of code and reuse it whenever you need it, without copying and pasting. This is one of the most important concepts in FP1 and the foundation of everything you’ll write from here on.
Table of Contents
What are Python functions?
A function is a named block of code that performs a specific task and can be called as many times as needed from anywhere in the program.
You’ve already been using functions without realising it. print(), input(), len(), range(), int(), round() — all of these are functions. The difference is that those are built into Python. Now you’ll create your own.
Why use functions?
Imagine you need to calculate the average of a list of grades in five different places in your program. Without functions you’d copy and paste the same three lines of code five times. If you later find a bug in that calculation you’d have to fix it in five places.
With a function you write it once and call it five times. If there’s a bug you fix it in one place:
# Without functions — repeated code
grades1 = [7.5, 8.0, 6.5]
total1 = sum(grades1)
average1 = total1 / len(grades1)
print(f"Group 1 average: {average1}")
grades2 = [9.0, 5.5, 7.0]
total2 = sum(grades2)
average2 = total2 / len(grades2)
print(f"Group 2 average: {average2}")
# With functions — write once, use everywhere
def calculate_average(grades):
return sum(grades) / len(grades)
print(f"Group 1 average: {calculate_average([7.5, 8.0, 6.5])}")
print(f"Group 2 average: {calculate_average([9.0, 5.5, 7.0])}")
How to define a function
The syntax to create a function in Python:
def function_name(parameters):
code_block
return value # optional
def— keyword that tells Python “I’m defining a function”function_name— the name you give the function. Same rules as variables: lowercase, no spaces, use underscores(parameters)— the inputs the function receives. Can be empty:():— mandatory colon, same asifandfor- indented block — the code that belongs to the function
return— the value the function sends back. Optional — if there’s noreturnthe function returnsNone
The simplest function — no parameters, no return
def print_separator():
print("=" * 30)
# Call it
print_separator()
print("Results")
print_separator()
Output:
============================== Results ==============================
The function does something (prints) but doesn’t receive anything and doesn’t return anything. Notice that def print_separator(): defines the function but doesn’t execute it. The function only runs when you call it with print_separator().
Functions with parameters
Parameters are the inputs a function receives, values you pass when calling it:
def greet(name):
print(f"Hello, {name}!")
greet("Sergio") # → Hello, Sergio!
greet("María") # → Hello, María!
greet("Carlos") # → Hello, Carlos!
name is the parameter, inside the function it works like a variable. When you call greet("Sergio"), name takes the value "Sergio". When you call greet("María"), name takes "María".
Multiple parameters:
def describe_student(name, age, degree):
print(f"{name} is {age} years old and studies {degree}")
describe_student("Sergio", 20, "GCID")
# → Sergio is 20 years old and studies GCID
Functions with return
When a function needs to give back a result you use return:
def square(n):
return n * n
result = square(5)
print(result) # → 25
print(square(3)) # → 9
print(square(4) + square(3)) # → 25
return does two things: it sends the value back to wherever the function was called from, and it exits the function immediately no code after return runs in that call.
A function can have multiple return statements it returns from the first one it reaches:
def classify_grade(grade):
if grade >= 9:
return "Outstanding"
elif grade >= 7:
return "Merit"
elif grade >= 5:
return "Passed"
else:
return "Failed"
print(classify_grade(8.5)) # → Merit
print(classify_grade(4.0)) # → Failed
Default parameters
You can give parameters a default value, if the caller doesn’t provide that argument it uses the default:
def power(base, exponent=2): # exponent defaults to 2
return base ** exponent
print(power(5)) # → 25 (5² — uses default exponent)
print(power(5, 3)) # → 125 (5³ — overrides default)
print(power(2, 10)) # → 1024 (2¹⁰)
Default parameters must come after non-default ones:
# WRONG
def power(exponent=2, base): # SyntaxError
...
# RIGHT
def power(base, exponent=2): # non-default first
...
Functions calling other functions
This is where functions really shine, you can build complex logic by combining simple functions:
def calculate_average(grades):
if len(grades) == 0:
return 0
return sum(grades) / len(grades)
def classify_grade(grade):
if grade >= 9: return "Outstanding"
elif grade >= 7: return "Merit"
elif grade >= 5: return "Passed"
else: return "Failed"
def analyse_group(group_name, grades):
average = calculate_average(grades) # calls calculate_average
classification = classify_grade(average) # calls classify_grade
print(f"\n{group_name}:")
print(f" Grades: {grades}")
print(f" Average: {round(average, 2)}")
print(f" Result: {classification}")
# Now one call does everything
analyse_group("FP1 Group A", [7.5, 8.0, 6.5, 9.0])
analyse_group("FP1 Group B", [5.5, 4.0, 6.0, 7.5])
Output:
FP1 Group A: Grades: [7.5, 8.0, 6.5, 9.0] Average: 7.75 Result: Merit FP1 Group B: Grades: [5.5, 4.0, 6.0, 7.5] Average: 5.75 Result: Passed
Scope — variables inside functions
Variables defined inside a function only exist inside that function. They’re invisible to the rest of the program:
def my_function():
x = 10 # local variable — only exists inside the function
print(x)
my_function() # → 10
print(x) # NameError: name 'x' is not defined
This is called scope. Local variables are created when the function is called and destroyed when it returns. This is why you can use the same variable name in different functions without them interfering with each other.
The most common mistakes with functions
Mistake 1 — Confusing defining with calling:
# This only DEFINES the function — doesn't run it
def greet():
print("Hello")
# This CALLS it — runs it
greet()
Mistake 2 — Forgetting return and using the result:
def add(a, b):
result = a + b
# Missing return!
total = add(3, 4)
print(total) # → None (not 7)
# Fix
def add(a, b):
return a + b
Mistake 3 — Modifying a global variable inside a function:
count = 0
def increment():
count += 1 # UnboundLocalError
# Fix — return the new value instead
def increment(current):
return current + 1
count = increment(count)
Mistake 4 — Wrong parameter order:
def subtract(a, b):
return a - b
print(subtract(10, 3)) # → 7
print(subtract(3, 10)) # → -7 (wrong order)
The order of arguments when calling must match the order of parameters in the definition.
Visualise it with Python Tutor
Functions are where pythontutor.com is most valuable. Paste this and step through it:
def square(n):
result = n * n
return result
def sum_of_squares(a, b):
sq_a = square(a)
sq_b = square(b)
return sq_a + sq_b
total = sum_of_squares(3, 4)
print(total)
Watch how when sum_of_squares is called a new frame opens. Then when it calls square, another frame opens on top. When square returns, its frame closes and control goes back to sum_of_squares. When sum_of_squares returns, its frame closes and control returns to the main program. That stack of frames is exactly how Python manages function calls.
Quick summary
# DEFINE A FUNCTION
def function_name(param1, param2):
code_block
return value # optional
# CALL A FUNCTION
result = function_name(arg1, arg2)
# WITHOUT PARAMETERS
def print_separator():
print("=" * 30)
print_separator()
# WITH PARAMETERS
def greet(name, age):
print(f"Hello {name}, you are {age}")
greet("Sergio", 20)
# WITH RETURN
def square(n):
return n * n
result = square(5) # → 25
# DEFAULT PARAMETERS
def power(base, exponent=2):
return base ** exponent
power(5) # → 25 (uses default)
power(5, 3) # → 125 (overrides default)
# FUNCTIONS CALLING FUNCTIONS
def average(grades):
return sum(grades) / len(grades)
def classify(grade):
if grade >= 5: return "Passed"
else: return "Failed"
def analyse(grades):
avg = average(grades) # calls average
cls = classify(avg) # calls classify
return avg, cls
# SCOPE
def my_function():
x = 10 # local — only exists inside
print(x) # NameError — x doesn't exist outside
# COMMON ERRORS
# 1. Define ≠ call — def only defines, () calls
# 2. Missing return → function returns None
# 3. count += 1 inside function → UnboundLocalError
# 4. Wrong argument order → wrong result, no error
In the next article we practice Python functions with real programs in VS Code.

2 Comments