Computer Programming 1 (Python) • Problem Solving & Mini-Projects

Building Utility Modules — I can build a small "utilities" module and import it into another script in supported tasks.

Learn to organize reusable Python functions into modules so your projects stay clean and efficient.

A Python coding terminal with a program flow and laptop.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

In the earliest days of computing, programs were small enough to fit in a single file. As software grew in complexity, developers quickly realized that copying and pasting the same code into every new project was error-prone and time-consuming. The idea of modular programming — breaking a program into self-contained, reusable pieces — became one of the most important breakthroughs in software engineering. Python, from its very first release, was designed with modules at its core, making it remarkably easy for programmers to organize and share code.

1960s • Structured Programming Emerges

Computer scientists like Edsger Dijkstra advocate breaking programs into smaller, manageable subroutines instead of one monolithic block of code. This lays the groundwork for modular thinking.

1972 • C Language & Header Files

The C programming language introduces header files and separate compilation units, allowing developers to split code across multiple files and reuse common functions.

1991 • Python 0.9.0 Released

Guido van Rossum releases the first version of Python, which includes a module system and the import statement from day one. Reusing code across scripts is a built-in feature, not an afterthought.

2000s • Package Managers & PyPI

The Python Package Index (PyPI) launches, enabling developers worldwide to publish and install reusable modules with a single command. The ecosystem grows to hundreds of thousands of packages.

Today • Modules Everywhere

Every serious Python project uses modules. Whether you use NumPy for math, Flask for web apps, or Pygame for games, you are importing someone else's utility module into your script.

The central question this lesson addresses is: How do you take the helper functions you keep rewriting and organize them into a reusable module? By the end, you will be able to create your own utility module and import it into any Python script you write.

SECTION 2

Core Principles & Definitions

Before you write your first utility module, you need to understand a handful of foundational ideas. A module in Python is simply a .py file that contains functions, variables, or classes you want to reuse. When another script needs that code, it uses the import statement to load the module's contents into memory. That is the entire concept at its most basic — one file defines helpers, another file borrows them.

1

Module

Any Python file (ending in .py) that contains reusable code — functions, constants, or classes. Example: utils.py
2

Import Statement

The keyword import tells Python to load a module so you can use its functions. You can import the whole module or specific items with from ... import ...
3

Namespace

A container that keeps names separate. When you write utils.clean_text(), the utils. prefix is the namespace — it prevents name collisions with your own functions.
4

The __name__ Guard

The pattern if __name__ == "__main__": lets you include test code in a module that only runs when the file is executed directly — not when it is imported.
5

Reusability

The core goal: write a function once, use it in many scripts. This reduces bugs, saves time, and keeps each file short and focused on its specific task.
✦

KEY TAKEAWAY

Think of a utility module like a toolbox in a workshop. Instead of buying new wrenches and screwdrivers for every project, you keep one well-organized toolbox and carry it to whichever workbench you need. A utility module is your code toolbox — build it once, then import it wherever you need those tools.

SECTION 3

Visual Explanation — How Importing Works

utils.py(Your Utility Module)def clean_text(s):return s.strip().lower()def fahrenheit_to_c(f):return (f - 32) * 5/9def is_even(n):return n % 2 == 0if __name__ == "__main__":print(clean_text(" HI "))⬆ Only runs when executed directlymain.py(Your Main Script)import utilsname = utils.clean_text(" Alice ")temp = utils.fahrenheit_to_c(98.6)import
The left box shows utils.py with three helper functions and a __name__ guard (dashed border). The right box shows main.py, which imports the module and calls its functions using dot notation. The arrow represents the import connection between the two files.

When Python encounters import utils inside main.py, it searches for a file called utils.py in the same directory (and a few other locations). Python then executes all the top-level code in utils.py — which defines the functions — and creates a module object that holds them. After that, you access any function by writing the module name, a dot, and the function name, such as utils.clean_text(). The dashed box at the bottom of utils.py represents the name guard, which ensures that any test code you write inside the module will not run when the module is imported — only when you run the file directly.

SECTION 4

How It Works — Import Mechanics

Three Ways to Import

Python gives you several import styles, and choosing the right one affects readability and namespace clarity. The most common approaches are whole-module import, selective import, and aliased import. Each one has strengths that depend on your project's needs.

Import StyleSyntaxHow You Call Functions
Whole-module importimport utilsutils.clean_text("hi")
Selective importfrom utils import clean_textclean_text("hi")
Aliased importimport utils as uu.clean_text("hi")
Wildcard import (avoid)from utils import *clean_text("hi")

Common import patterns in Python

The Import Search Path

When Python sees an import statement, it searches for the module in a specific order. First, it checks the directory where the calling script lives. Next, it looks through folders listed in the sys.path variable, which includes the Python standard library and any installed packages. For this lesson, the simplest setup is to keep your utility module in the same folder as your main script. That way, Python finds it immediately with no extra configuration.

The __name__ Guard Explained

Every Python file has a hidden variable called __name__. When you run a file directly (e.g., python utils.py), Python sets __name__ to the string "__main__". But when another script imports that file, __name__ is set to the module's actual name (like "utils"). By wrapping test prints inside if __name__ == "__main__":, you ensure those lines only execute during direct testing, keeping your imports clean and side-effect-free.

⚠

Common Mistake

Using from utils import * (the wildcard import) dumps every name from the module into your script's namespace. This can cause confusing bugs if two modules define functions with the same name. Stick with import utils or from utils import specific_function for clarity.

SECTION 5

Anatomy of a Utility Module

A well-structured utility module follows a predictable pattern. Understanding this pattern helps you write modules that are easy to read, test, and import. The diagram below shows the four main sections of a typical utility module file, from top to bottom.

utils.py — File Structure① Docstring"""Utility functions for text and math."""Describes the module's purpose② ConstantsPI = 3.14159Reusable values that don't change③ Function Definitionsdef clean_text(s):"""Remove whitespace, lowercase."""return s.strip().lower()def fahrenheit_to_c(f):"""Convert °F to °C."""return round((f - 32) * 5 / 9, 2)The core of your module — each function has a docstring④ Name Guard (optional but recommended)if __name__ == "__main__":print(clean_text(" HELLO "))Test code that only runs when the file is executed directly
A utility module typically includes four layers: a docstring at the top, then constants, followed by function definitions (each with its own docstring), and finally a name guard for testing.

Best Practices for Utility Modules

  • ✓ Give the file a descriptive name — text_helpers.py is clearer than stuff.py. Use lowercase and underscores (snake_case).
  • ✓ Write a docstring for every function — A short triple-quoted string right after def tells other programmers (and future you) what the function does.
  • ✓ Keep functions focused — Each function should do one thing well. If a function is getting long, break it into smaller helpers.
  • ✓ Always include the name guard — Even if you don't have test code yet, include the if __name__ == "__main__": block as a placeholder for future testing.

SECTION 6

Worked Example — Build and Use a Utility Module

Let's build a complete utility module from scratch and then import it into a main script. Imagine you are creating a small quiz app and you need helper functions to format questions, validate answers, and calculate scores.

Building quiz_utils.py and Using It in quiz.py

  1. 1

    Step 1 — Plan Your Functions

    Before writing any code, decide what reusable tasks your project needs. For a quiz app, we need: (1) a function to format a question with a number, (2) a function to check if the user's answer matches the correct answer (ignoring case and whitespace), and (3) a function to calculate a percentage score.
  2. 2

    Step 2 — Create the Module File

    Create a new file called quiz_utils.py and write the module docstring and function definitions: """Utility functions for the quiz app.""" def format_question(number, text): """Return a numbered question string.""" return f"Q{number}: {text}" def check_answer(user_answer, correct_answer): """Return True if answers match (case-insensitive).""" return user_answer.strip().lower() == correct_answer.strip().lower() def calc_percentage(correct, total): """Return the score as a percentage, rounded to 1 decimal.""" return round((correct / total) * 100, 1)
    File saved: quiz_utils.py with three functions.
  3. 3

    Step 3 — Add the Name Guard

    At the bottom of quiz_utils.py, add test code inside the name guard: if __name__ == "__main__": print(format_question(1, "What is 2+2?")) print(check_answer(" Paris ", "paris")) print(calc_percentage(7, 10))
    Running python quiz_utils.py outputs: Q1: What is 2+2?, True, 70.0
  4. 4

    Step 4 — Create the Main Script

    In the same folder, create quiz.py and import the module: import quiz_utils questions = ["Capital of France?", "5 * 6?", "Color of the sky?"] answers = ["paris", "30", "blue"] score = 0 for i, q in enumerate(questions): print(quiz_utils.format_question(i + 1, q)) user = input("Your answer: ") if quiz_utils.check_answer(user, answers[i]): score += 1 pct = quiz_utils.calc_percentage(score, len(questions)) print(f"You scored {pct}%!")
    quiz.py successfully uses all three functions from quiz_utils.py. None of the test prints from the name guard run when importing.
  5. 5

    Step 5 — Verify the Folder Structure

    Your project folder should look like this: my_quiz_project/ quiz_utils.py ← utility module quiz.py ← main script Both files must be in the same directory for the import to work without extra configuration.
    Run with python quiz.py and the quiz starts, using your utility functions behind the scenes.

SECTION 7

Import Styles — Strengths & Trade-offs

We saw in Section 4 that Python offers several import styles. Each one has strengths and trade-offs that matter as your projects grow. The table below compares the four most common approaches so you can choose wisely.

Import StyleStrengthsLimitations
import utilsKeeps the namespace clean. Easy to see which module a function comes from. Best for beginners.Slightly more typing because you use utils. every time.
from utils import clean_textShorter calls — just clean_text(). Only loads what you need.Could clash with a local function of the same name. Harder to tell where the function came from in large files.
import utils as uShortens the module name while keeping namespace clarity. Great for modules with long names.Aliases can be confusing if they are not descriptive. Others reading your code need to remember the alias.
from utils import *Very short calls. Convenient for quick prototyping.Avoid in real projects. Pollutes the namespace. Easy to create hard-to-find bugs.

Comparison of Python import styles

✦

KEY TAKEAWAY

Think of import styles like addressing a letter. import utils is like using a full address — clear and unambiguous. from utils import clean_text is like using a nickname — shorter, but only works if everyone knows who you mean. For most school projects, the whole-module import is the safest and clearest choice.

SECTION 8

Connection to Advanced Topics — Packages & Libraries

The utility module you built in this lesson is the simplest building block of Python's code-sharing ecosystem. As your projects grow, you will encounter more powerful organizational tools. Understanding where single-file modules fit in the bigger picture will help you level up when you are ready.

FeatureSingle Module (This Lesson)Package (Next Level)
StructureOne .py fileA folder with an __init__.py and multiple .py files
Import syntaximport utilsfrom mypackage import utils
Best forSmall projects with a handful of helper functionsLarge projects with hundreds of functions organized by category
DistributionCopy the file into each project folderInstall via pip install from PyPI
ExampleYour quiz_utils.pyThe requests library for HTTP

Single modules vs. packages

Every massive Python library — from numpy to django — started as a collection of simple modules just like the ones you are learning to build now. Mastering single-file modules gives you the foundation to eventually create packages, publish them to PyPI, and share your code with the world. For now, focus on writing clean, well-documented utility modules — the skills transfer directly.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain in your own words why you would create a utility module instead of writing all your functions directly inside your main script. Give at least two specific benefits.
Show answer
Creating a utility module keeps your main script short and focused on its primary purpose, making it easier to read and debug. It also enables reusability — you can import the same module into multiple scripts without copying and pasting code. If you need to fix a bug in a helper function, you only fix it in one place (the module), and every script that imports it gets the fix automatically. This separation of concerns is a core principle of good software design.

Question 2: BASIC CALCULATION

You have a file called math_helpers.py containing a function def circle_area(r): return 3.14159 * r ** 2. Write the exact two lines of code you would put in main.py to import the module and print the area of a circle with radius 5.
Show answer
You would write: import math_helpers on the first line, then print(math_helpers.circle_area(5)) on the second line. The output would be 78.53975 because 3.14159 × 5² = 3.14159 × 25 = 78.53975. Alternatively, you could use from math_helpers import circle_area and then call print(circle_area(5)) without the module prefix.

Question 3: INTERMEDIATE

A student writes the following in helpers.py: def greet(name): return f"Hello, {name}!" print(greet("World")) When they run import helpers in another script, the message "Hello, World!" unexpectedly prints to the console. Identify the bug and rewrite the bottom of helpers.py to fix it.
Show answer
The bug is that print(greet("World")) sits at the top level of the module, so it executes every time the file is imported. The fix is to wrap that line inside the name guard: if __name__ == "__main__": followed by print(greet("World")). Now the print only runs when you execute python helpers.py directly, not when you import the module into another script. This is one of the most common mistakes beginners make with modules.

Question 4: APPLIED

You are building a to-do list app. Create a utility module called todo_utils.py with the following three functions: (1) add_task(task_list, task) — appends a task string to the list and returns the updated list; (2) remove_task(task_list, index) — removes the task at the given index and returns the updated list (handle an invalid index by returning the list unchanged); (3) show_tasks(task_list) — returns a formatted, numbered string of all tasks. Include a name guard that tests all three functions.
Show answer
Here is one correct solution: """Utility functions for the to-do list app.""" def add_task(task_list, task): task_list.append(task) return task_list def remove_task(task_list, index): if 0 <= index < len(task_list): task_list.pop(index) return task_list def show_tasks(task_list): return "\n".join(f"{i+1}. {t}" for i, t in enumerate(task_list)) if __name__ == "__main__": todos = add_task([], "Buy milk") todos = add_task(todos, "Do homework") print(show_tasks(todos)) todos = remove_task(todos, 0) print(show_tasks(todos)) The key design choices are: the index validation in remove_task prevents crashes from bad input, each function returns the updated list for easy chaining, and the name guard tests all three functions when you run the file directly.

Question 5: CRITICAL THINKING

A classmate argues: "I don't need modules — I can just copy-paste my functions into every script." Write a response that uses a real-world scenario to explain why this approach fails as projects grow. Then, describe a situation where it might actually be fine to keep everything in a single file.
Show answer
Imagine you wrote a clean_text() function and pasted it into 10 different scripts. Later you discover a bug — it doesn't handle empty strings correctly. Now you have to find and fix the same bug in all 10 files, and if you miss even one, that script still has the bug. With a module, you fix it in one place and every script benefits. This is especially important when multiple people work on the same project, because copy-paste leads to inconsistent versions of the same function. However, keeping everything in a single file is perfectly reasonable for a very small, one-off script — like a quick homework assignment with 20 lines of code. In that case, the overhead of creating a separate module adds complexity without much benefit. The rule of thumb is: if you use the same function in two or more scripts, it belongs in a module.

SUMMARY

Lesson Summary

Summary

A utility module is simply a .py file containing reusable functions, constants, or classes. You create one by writing your helper functions in a separate file (like utils.py), adding docstrings to each function, and including the __name__ guard at the bottom so your test code doesn't run on import. To use the module, you place both files in the same folder and write import utils at the top of your main script, then call functions using dot notation like utils.clean_text().

Python offers several import styles — whole-module, selective, and aliased — each with trade-offs in clarity and convenience. The whole-module import is recommended for most school projects because it keeps namespaces clean. Building utility modules is the first step toward understanding packages and third-party libraries — the core of Python's massive ecosystem. Master this pattern and you will write cleaner, more maintainable code in every future project.

Varsity Tutors • Computer Programming 1 (Python) • Building Utility Modules