Back to Topics
Term 3 DI10-4

Functions & Modularity

Learning Objectives

  • Define and call functions
  • Use parameters and return values
  • Understand variable scope
  • Apply modular design principles

What is a Function?

A function is a reusable block of code that performs a specific task. Functions help organize code, avoid repetition, and make programs easier to understand.

Analogy: A function is like a recipe. You define it once (write the recipe), and you can use it many times (cook the dish) whenever you need it.

Defining Functions

A function definition includes:

  • Name - What the function is called
  • Parameters - Input values (optional)
  • Body - The code that runs
  • Return value - The output (optional)
# Python function syntax
def function_name(parameters):
    # Function body
    return value

# Example: Simple greeting function
def greet():
    print("Hello, World!")

# Call the function
greet()  # Output: Hello, World!

Parameters and Arguments

Parameters are variables listed in the function definition. Arguments are the actual values passed when calling the function.

# Function with one parameter
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")    # Output: Hello, Alice!
greet("Bob")      # Output: Hello, Bob!

# Function with multiple parameters
def add(a, b):
    result = a + b
    print(f"{a} + {b} = {result}")

add(5, 3)         # Output: 5 + 3 = 8
add(10, 20)       # Output: 10 + 20 = 30

Default Parameters

# Parameters can have default values
def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Alice")              # Output: Hello, Alice!
greet("Bob", "Good morning") # Output: Good morning, Bob!

Return Values

Functions can return a value back to where they were called. This allows the result to be stored or used in other calculations.

# Function that returns a value
def add(a, b):
    return a + b

# Store the result
result = add(5, 3)
print(result)  # Output: 8

# Use directly in expressions
total = add(10, 20) + add(5, 5)
print(total)   # Output: 40

# Multiple uses
def calculate_area(length, width):
    return length * width

room1 = calculate_area(5, 4)   # 20
room2 = calculate_area(6, 3)   # 18
total_area = room1 + room2     # 38

print() vs return:

  • print() displays text on screen but returns nothing
  • return sends a value back that can be stored and used
  • • Use return when you need to use the result elsewhere

Variable Scope

Scope determines where a variable can be accessed in your code.

Local Variables

Variables created inside a function only exist inside that function.

def my_function():
    x = 10  # Local variable
    print(x)

my_function()  # Output: 10
print(x)       # ERROR! x doesn't exist outside the function

Global Variables

Variables created outside functions can be accessed anywhere.

name = "Alice"  # Global variable

def greet():
    print(f"Hello, {name}")  # Can access global variable

greet()     # Output: Hello, Alice
print(name) # Output: Alice

Best Practice: Avoid relying on global variables inside functions. Instead, pass values as parameters and return results. This makes functions more predictable and reusable.

Modularity

Modularity means breaking a program into smaller, independent parts (modules or functions) that each handle one specific task.

Benefits of Modular Design

  • Reusability - Functions can be used multiple times
  • Readability - Code is easier to understand
  • Testing - Each part can be tested independently
  • Maintenance - Easier to find and fix bugs
  • Collaboration - Different people can work on different parts

Example: Modular Program

# A calculator program with modular design

def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

def multiply(a, b):
    return a * b

def divide(a, b):
    if b == 0:
        return "Error: Cannot divide by zero"
    return a / b

def get_numbers():
    num1 = float(input("Enter first number: "))
    num2 = float(input("Enter second number: "))
    return num1, num2

def show_menu():
    print("\n--- Calculator ---")
    print("1. Add")
    print("2. Subtract")
    print("3. Multiply")
    print("4. Divide")
    print("5. Quit")

def main():
    while True:
        show_menu()
        choice = input("Choose an option: ")

        if choice == "5":
            print("Goodbye!")
            break

        num1, num2 = get_numbers()

        if choice == "1":
            print(f"Result: {add(num1, num2)}")
        elif choice == "2":
            print(f"Result: {subtract(num1, num2)}")
        elif choice == "3":
            print(f"Result: {multiply(num1, num2)}")
        elif choice == "4":
            print(f"Result: {divide(num1, num2)}")

# Run the program
main()

Built-in Functions

Programming languages come with many pre-built functions:

Function Purpose Example
print() Display output print("Hello")
input() Get user input name = input("Name: ")
len() Get length len([1,2,3]) → 3
int() Convert to integer int("42") → 42
str() Convert to string str(42) → "42"
range() Generate number sequence range(1, 5) → 1,2,3,4
max() / min() Find largest/smallest max(3, 7, 2) → 7
sum() Add all items sum([1,2,3]) → 6

Key Terminology

  • Function - A reusable block of code that performs a specific task
  • Parameter - Variable in a function definition that receives input
  • Argument - Actual value passed to a function when calling it
  • Return value - The output a function sends back
  • Scope - Where a variable can be accessed (local vs global)
  • Modularity - Breaking code into independent, reusable parts
  • Call - Running/executing a function
🏠

Project Connection

In Simpson's House...

The Simpson's House Python code is a textbook example of modular design. Each device has its own function, on_message() calls them without duplicating logic, and adding a new device means writing one new function — not rewriting the whole program.

Single Responsibility

control_light(), control_servo(), control_garage() — one job each

Each function does exactly one thing. control_light() only controls the LED. control_servo() only moves the door servo. Clean, readable, easy to fix.

Parameters

Functions receive the command as a parameter

def control_light(command): — the function doesn't read from MQTT directly; it receives the payload as a parameter. This makes it reusable and testable independently.

The Dispatcher Pattern

on_message() routes calls — doesn't do the work itself

on_message() decodes the topic and calls the right function. It's a dispatcher — it coordinates but doesn't duplicate logic. This is modularity in action.

Extensibility

Adding a new device = writing one new function

Want to add a fan? Write control_fan(command), add one IF branch to on_message(), and subscribe to a new topic. The existing code doesn't change — this is the power of modular design.

Explore the full Simpson's House project →