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 | Nonefor 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 operationsandimport 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
assertto 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:
- Same directory as main.py ✓
- Standard library
- Installed packages
Since all files are in the same directory, imports work automatically.
🚀 Specification Challenge
Your calculator is working! Now extend it:
- Add a new operation to
operations.py— for example,modulo()(remainder) orabsolute_value() - Add the operation to
main.py— add it to the menu and create a branch for it - 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 | Nonefor 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."