Skip to main content

Building a Calculator Utility (Capstone Project)

Capstone Project Overview

The Task: Build a multi-module calculator application that demonstrates:

  • Separation of concerns: Different files handle different responsibilities
  • Module imports: Main program imports and uses custom modules
  • Function specifications: Clear type hints and docstrings
  • Parameter patterns: Functions with required and optional parameters
  • Return values: Functions returning single values, tuples, and optional results
  • Testing: Validating that functions work correctly

Project Structure:

calculator/
├── operations.py # Mathematical operations
├── utils.py # Utility functions (I/O, validation, display)
├── main.py # Main program orchestrating the calculator
└── test_calculator.py # Tests for the operations

💬 AI Colearning Prompt

Ask your AI: "I'm building a calculator project with multiple modules. What's the best way to organize this? Should operations be in one file, utilities in another, and main orchestrate them?"

Expected Understanding: You see that module organization is a design choice with clear benefits: testability, reusability, clarity.

🎓 Instructor Commentary

You just built a professional Python project. Notice: each file has a clear purpose. Operations do math. Utils handle I/O. Main orchestrates. This is the separation of concerns principle. It makes code easier to test, modify, and reuse. When your projects grow, this organization scales. A 100-line script can be in one file. A 10,000-line application needs modular organization. You're learning that pattern now, at scale you can handle.


Step 1: Create operations.py — Mathematical Operations

This module contains pure functions that perform calculations. No side effects (no printing, no modifying global state).

💻 Code Idea: Operations Module

Loading Python environment...

Design Choices:

  • All functions are pure (no side effects)
  • Type hints are explicit
  • Functions that can fail return Type | None
  • Docstrings are minimal but complete

Step 2: Create utils.py — Utility Functions

This module handles user interaction, input validation, and output formatting.

💻 Code Idea: Utilities Module

Loading Python environment...

Design Choices:

  • Input functions handle validation
  • Return Type | None for operations that might fail
  • Display functions have no return value (-> None)
  • Clear separation: utils handles I/O, operations does math

Step 3: Create main.py — Main Program

This file orchestrates the calculator by importing and using the other modules.

💻 Code Idea: Main Program

Loading Python environment...

Key Patterns:

  • if __name__ == "__main__": Program only runs if executed directly (not imported)
  • import operations and import utils: Imports custom modules
  • Function calls like operations.add() show module.function pattern
  • Logic is clear: display menu, get input, call operation, display result

Step 4: Create test_calculator.py — Testing

This file validates that functions in operations.py work correctly.

💻 Code Idea: Test Module

Loading Python environment...

Output:

✓ test_add PASSED
✓ test_subtract PASSED
✓ test_multiply PASSED
✓ test_divide PASSED
✓ test_power PASSED
✓ test_square_root PASSED

✓ All tests passed!

Testing Pattern:

  • Each test function checks one operation
  • Use assert to verify expected behavior
  • Test normal cases, edge cases, and error cases
  • Run all tests to validate the project

How to Run the Project

Run the Calculator (Interactive)

# Navigate to project directory
cd calculator/

# Run the main program
python main.py

The calculator will display a menu. Choose operation (1-7) and enter numbers.

Run the Tests (Validation)

# Run tests to verify operations work
python test_calculator.py

# Output should show:
# ✓ test_add PASSED
# ✓ test_subtract PASSED
# ... (all tests)
# ✓ All tests passed!

Understanding the Project Structure

Why Separate Files?

operations.py — Pure Functions

  • Does: Mathematical calculations
  • Doesn't: Print, read input, modify global state
  • Benefit: Easy to test, easy to reuse in other projects

utils.py — I/O and Validation

  • Does: User interaction, input validation, display formatting
  • Doesn't: Do math (that's operations.py)
  • Benefit: Can be reused for other programs that need input/output

main.py — Orchestration

  • Does: Import and coordinate other modules
  • Doesn't: Do math or handle I/O directly
  • Benefit: Clear, readable flow of program logic

test_calculator.py — Verification

  • Does: Test operations.py functions
  • Benefit: Confidence that functions work before using them

Module Imports — How It Works

Loading Python environment...

Python searches for operations.py and utils.py in:

  1. Same directory as main.py ✓
  2. Standard library
  3. Installed packages

Since all files are in the same directory, imports work automatically.


🚀 Specification Challenge

Your calculator is working! Now extend it:

  1. Add a new operation to operations.py — for example, modulo() (remainder) or absolute_value()
  2. Add the operation to main.py — add it to the menu and create a branch for it
  3. Add a test for your new operation to test_calculator.py

This teaches you how good module design enables extensibility without breaking existing code.


✨ AI Tool Tip

Your calculator imports operations like import operations. This works because Python looks for operations.py in the same directory. When sharing code:

  • Same directory: Use import operations
  • Different directories: Use packages with __init__.py (more advanced, Chapter 29+)
  • Not sure: Ask your AI: "How do I organize Python modules for sharing?"

Try With AI

Build a complete multi-module calculator project integrating all Chapter 25 concepts.

🔍 Explore Module Architecture:

"Design a calculator with operations.py (math functions), utils.py (I/O), main.py (orchestration), and test_calculator.py (validation). For each module, define its responsibilities and explain why this separation improves testability and reusability."

🎯 Practice Implementation:

"Implement operations.py with add, subtract, multiply, divide, power, and square_root. Use type hints like def divide(a: float, b: float) -> float | None for edge cases. Write test_calculator.py with 3+ test cases per operation covering normal, edge, and error cases."

🧪 Test Separation of Concerns:

"I want to add calculation history (store last 10 calculations) and result formatting (currency, percentage, scientific). For each feature, decide which module should own it: operations.py, utils.py, or a new module. Implement one feature demonstrating proper separation."

🚀 Apply Complete Integration:

"Build working main.py that imports operations and utils, displays menu (1-7), handles user input with validation, calls correct operations, and exits gracefully. Add modulo and absolute_value operations. Refactor using plugin architecture: OPERATIONS = {'1': ('Addition', operations.add, 2)} to eliminate if/elif chain."