Sitemap
Ayoade Akintayo (PhD)

Ayoade Akintayo (PhD)

Computer Science lecturer and AI researcher. I write about ML, DL, and how intelligent systems are shaping the world, from classrooms to real-world applications

Computer Programming — Week 3

10 min readNov 13, 2025

--

Functions, Modular Programming, and Code Reuse

1. Concept of Functions

Thus far, we have written code that executes sequentially from top to bottom. While this is suitable for small scripts, it becomes unmanageable for larger, more complex programs. This is where the concept of a function becomes essential. A function is a self-contained block of code that is designed to perform a specific, well-defined task. Think of it as a specialized machine in a factory: you feed it raw materials (inputs), it performs a specific operation, and it produces a finished part (an output).

The motivation for using functions is threefold:

To avoid code duplication (the “Don’t Repeat Yourself” or DRY principle),

To enhance code readability by breaking down complex problems into logical, named units, and

To simplify debugging and maintenance by isolating functionality.

The process involves two key steps: defining a function and calling it. Defining a function involves using the def keyword in Python, followed by the function name, parentheses (), and a colon. The code block within the function is indented.

Calling a function is simply using its name followed by parentheses elsewhere in your code, which instructs the program to execute the function’s code block.

For example, consider an e-commerce checkout system that needs to calculate tax for every order. Without a function, the tax calculation logic would be copied and pasted every time it’s needed, leading to a maintenance nightmare. If the tax rate changes, you would have to find and update every instance. With a function, you define calculate_tax(order_subtotal) once. This function contains the logic to compute the tax. Then, anywhere in your checkout process, for the main order, for gift wrapping, for shipping, you simply call this function. This ensures consistency and makes updates trivial, as you only need to change the formula in one single location.

2. Types of Functions

In Python, functions can be broadly categorized into three types.

First, we have built-in functions. These are functions that are available by default in Python, without the need for any import statements. Examples include len() to find the length of a sequence, max() to find the largest item in an iterable, and range() to generate a sequence of numbers. They are the foundational tools provided by the language itself.

Second, we have user-defined functions. These are the functions that you, the programmer, create using the def keyword to organize your own code and logic, exactly like the calculate_tax() example we just discussed. They are the primary building blocks of your applications.

Third, we have lambda functions, also known as anonymous functions. These are small, single-expression functions defined using the lambda keyword. They are not declared with the standard def and are typically used for short, simple operations that are convenient to define inline. For instance, lambda x: x * 2 is a lambda function that doubles its input. They are often used in conjunction with functions like sort() or filter() where a simple, one-off function is required.

3. Function Parameters and Return Values

The power of functions is greatly enhanced by their ability to accept inputs and return outputs. Inputs are provided through parameters, which are the variables listed in the function’s definition. When you call a function, you provide arguments, which are the actual values you pass into those parameters.

Python offers great flexibility in how you pass arguments. Positional arguments are matched to parameters based on their order. Keyword arguments are specified by explicitly naming the parameter, which allows you to pass them in any order. You can also set default arguments in the function definition, which gives parameters a default value if no argument is provided during the function call. Finally, variable-length arguments (using *args for a list of positional arguments and **kwargs for a dictionary of keyword arguments) allow a function to accept an arbitrary number of arguments.

The output of a function is specified by the return statement. A function can return one or more values, and once a return statement is executed, the function exits, passing the value(s) back to the caller.

Consider a function to compute interest. We could define it as compute_interest(principal, rate, years=1). Here, principal and rate are required positional arguments, while years has a default value of 1. This design is both flexible and user-friendly. A user can call compute_interest(1000, 0.05) to calculate interest for one year, or they can call compute_interest(1000, 0.05, 5) to calculate it for five years. The function would perform the calculation and return the result, which the calling code could then use, for instance, to display to a user or store in a database.

4. Variable Scope and Lifetime

A critical concept when working with functions is variable scope, which determines where in your code a variable can be accessed. A local variable is defined inside a function and is only accessible within that function. Its lifetime begins when the function is called and ends when the function exits. In contrast, a global variable is defined outside of any function, in the main body of your code, and is accessible from anywhere in the module.

While global variables are powerful, their misuse is a common source of bugs. If multiple functions can read and write to the same global variable, it can become very difficult to track how and when its value changes, leading to unpredictable program behavior. This is especially problematic in large, multi-module programs. The best practice is to minimize the use of global variables. Favor passing values into functions as parameters and receiving results back via return values. This makes your functions more self-contained, predictable, and easier to test, as their behavior depends solely on their inputs, not on some hidden external state.

5. Modular Programming

As projects grow beyond a few hundred lines, managing all your code in a single file becomes impractical. Modular programming is the natural extension of functions: it involves dividing a program into separate, cohesive modules or files. Each module is a Python file (with a .py extension) that contains a collection of related functions, classes, and variables. The importance of this approach cannot be overstated; it promotes code reuse, allows multiple developers to work on different parts of a project simultaneously, and makes the system easier to navigate and understand.

Press enter or click to view image in full size

You use a module with the import statement. Python has a vast standard library of built-in modules, such as math for mathematical functions, os for interacting with the operating system, and datetime for working with dates and times. You can also create your own custom modules. For example, you could create a calculations.py file containing your financial functions and then use it in another script with import calculations.

Consider the architecture of banking software. It would be chaotic to have all the code for user login, money transfers, and statement generation in one massive file. A modular design would have a separate authentication.py module handling login validation, a transactions.py module for processing deposits and withdrawals, and a reporting.py module for generating statements. The main application would then import and orchestrate these modules. This separation of concerns means that if the bank needs to update its login security, developers can focus solely on the authentication.py module without worrying about accidentally breaking the transaction logic.

6. Documentation and Readability

Writing code that works is only half the battle; writing code that others (and your future self) can easily understand is the other. Readable code is maintainable code. This involves using clear, descriptive names for variables and functions, writing meaningful comments to explain the why behind complex logic (not the what, which should be evident from the code itself), and using docstrings.

Get Ayoade Akintayo (PhD)’s stories in your inbox

Join Medium for free to get updates from this writer.

A docstring is a multi-line string literal placed right after the def statement of a function (or at the top of a module or class). It is used to formally document the function’s purpose, its parameters, and its return value. Python has an established style guide known as PEP 8. It provides conventions for code layout, naming, and other stylistic elements to ensure consistency across the Python ecosystem. Adhering to PEP 8, using docstrings, and writing clear comments are hallmarks of a professional programmer and are crucial for collaboration in any software development team.

Classroom Activities

Pair Programming: Reusable Function Library: In this session, you will work in pairs to design and implement a small, reusable library of mathematical functions. One of you will act as the “driver,” writing the code, while the other will be the “navigator,” reviewing each line and planning the next steps. You will create functions for operations like finding the greatest common divisor (GCD), the least common multiple (LCM), and perhaps a function to solve quadratic equations. This exercise will give you practical experience in function design and collaborative coding.

Mini Debate: “Is modular programming always beneficial?” We will then hold a short, structured debate. One side will argue that modular programming introduces unnecessary overhead for small, single-developer scripts. The other will argue that cultivating the habit of modular design, even in small projects, pays dividends in code clarity and prepares one for larger-scale development. The goal is to critically evaluate the costs and benefits of software design principles.

Hands-On Lab: Writing and Using Functions

The lab this week is designed to solidify your understanding of functions and modules through practical implementation.

Tasks:

  1. Prime Number Function: Write a function called is_prime(n) that takes an integer n as an argument and returns True if the number is prime and False otherwise. This will require using loops and conditionals within the function body.
def is_prime(n):
# Prime numbers are greater than 1
if n <= 1:
return False

# Check if n has any divisors other than 1 and itself
for i in range(2, n):
if n % i == 0:
return False

return True

# Example usage
print(is_prime(2)) # True
print(is_prime(9)) # False
print(is_prime(11)) # True

2. Create a Custom Module: Create a new Python file named math_utils.py. In this file, define at least three utility functions, such as factorial(n), gcd(a, b), and average(numbers).

# math_utils.py
# A custom math utility module

def factorial(n):
"""Return the factorial of a non-negative integer n."""
if n < 0:
raise ValueError("Factorial is not defined for negative numbers.")
result = 1
for i in range(1, n + 1):
result *= i
return result


def gcd(a, b):
"""Return the greatest common divisor (GCD) of two integers."""
while b != 0:
a, b = b, a % b
return a


def average(numbers):
"""Return the average (mean) of a list of numbers."""
if not numbers:
raise ValueError("Cannot compute average of an empty list.")
return sum(numbers) / len(numbers)

3. Import and Use the Module: Create a separate Python file (e.g., main.py). In this file, use the import statement to import your math_utils module. Then, write code that calls the functions from the module and displays the results. This demonstrates how code can be organized and reused across different parts of an application.

import math_utils

print(math_utils.factorial(5)) # Output: 120
print(math_utils.gcd(48, 18)) # Output: 6
print(math_utils.average([10, 20, 30])) # Output: 20.0

Assignment 3: “Modular Code Design”

Objective: To design and implement a multi-module Python application, demonstrating an understanding of functions, code separation, and documentation.

Deliverables: You will develop a Python application split across three files (modules):

1. auth.py: This module will be responsible for user login validation. It should contain a function, for example, validate_login(username, password), which checks the credentials against a hard-coded dictionary of users and returns True or False.

2. finance.py: This module will perform basic financial calculations. It should contain at least two functions, such as calculate_loan_payment(principal, rate, term) and calculate_compound_interest(principal, rate, years).

3. main.py: This is the main application script. It will import the auth and finance modules. It should simulate a login process using the auth module and, upon successful login, present a menu to the user to choose one of the financial calculations from the finance module.

Your code must include comments explaining the logic and docstrings for every function you define. You must also submit screenshots showing the program running successfully.

Case Study: Modularity in Healthcare Systems

Industry: Healthcare Scenario: Consider a comprehensive Hospital Management System used by a large hospital. This software handles patient records, appointment scheduling, billing, pharmacy inventory, and lab results. Implementing all this in a single, monolithic codebase would be a recipe for disaster. Instead, it uses a highly modular design. There is a dedicated patient_records module that manages all patient data, a billing module that generates invoices and processes insurance claims, and an inventory module that tracks medical supplies and drugs. These modules can be developed, tested, and updated independently. For instance, when new government billing regulations are introduced, the hospital’s IT team can update the billing module without touching the sensitive and complex patient records logic. This modularity is not a luxury but a necessity, as it enables the system to scale with the hospital’s needs and remain maintainable over a long lifespan, which is critical for software that supports essential healthcare services.

Reflection / Discussion Question

“How does modular programming contribute to software scalability and long-term maintenance in enterprise systems?”

Reflect on the healthcare case study and your own assignment. Consider how separating concerns into modules makes it easier to onboard new developers, fix bugs without causing cascading errors, and add new features by building upon or extending existing modules without rewriting the entire system.

Summary / Key Takeaways

Functions are fundamental units of code that promote reusability, readability, and easier debugging by encapsulating specific tasks.

Python supports built-in, user-defined, and lambda (anonymous) functions, each serving different purposes.

Functions accept inputs via parameters (using positional, keyword, default, or variable-length arguments) and return outputs using the return statement.

Understanding variable scope (local vs. global) is crucial for writing predictable and bug-free code.

Modular programming involves breaking a large program into separate files (modules), which is essential for managing complexity, enabling teamwork, and building scalable systems.

Documentation through comments, docstrings, and adherence to style guides like PEP 8 is a critical practice for writing professional, maintainable code that can be understood and extended by others.

--

--

Ayoade Akintayo (PhD)
Ayoade Akintayo (PhD)