Assignment Overview
In this assignment, you will build a comprehensive Calculator Library that demonstrates your mastery of Python functions. This project requires you to apply ALL concepts from Module 5: function definitions, multiple parameter types, return values, lambda expressions, higher-order functions, closures, decorators, and recursive algorithms.
math module.
No third-party libraries allowed. This tests your understanding of pure Python functions.
Basic Functions (5.1)
Function definitions, parameters, arguments, return values, docstrings
Advanced Functions (5.2)
*args, **kwargs, lambda, closures, decorators, recursion
Variable Scope (5.3)
Local, global, nonlocal, LEGB rule, closures
The Scenario
MathGenius Calculator Library
You have been hired as a Python Developer at MathGenius Solutions, a company that creates educational software. The lead developer has given you this task:
"We need a reusable calculator library that showcases different types of Python functions. It should include basic arithmetic, statistical calculations, financial computations, and mathematical sequences. Make sure to use decorators for logging and validation, and implement some algorithms recursively."
Your Task
Create a Python file called calculator_library.py that implements a complete
calculator library with various function types. Your code must demonstrate proficiency in
all function concepts taught in Module 5, including proper docstrings and type hints.
Requirements
Your calculator_library.py must implement ALL of the following functions.
Each function is mandatory and will be tested individually.
Basic Arithmetic Functions
Create four basic functions with proper docstrings:
add(a, b)- Returns the sum of two numberssubtract(a, b)- Returns the difference of two numbersmultiply(a, b)- Returns the product of two numbersdivide(a, b)- Returns the quotient, handles division by zero
def add(a: float, b: float) -> float:
"""
Add two numbers together.
Args:
a: First number
b: Second number
Returns:
The sum of a and b
"""
# Your implementation here
pass
Function with Default Parameters
Create a function power(base, exponent=2) that:
- Raises a number to a power
- Default exponent is 2 (squares the number)
- Works with negative exponents
def power(base: float, exponent: float = 2) -> float:
"""Raise base to the power of exponent. Default squares the base."""
pass
Function with *args
Create a function calculate_sum(*numbers) that:
- Accepts any number of numeric arguments
- Returns the sum of all provided numbers
- Returns 0 if no arguments provided
def calculate_sum(*numbers) -> float:
"""Calculate the sum of any number of arguments."""
# Example: calculate_sum(1, 2, 3, 4, 5) -> 15
pass
Function with **kwargs
Create a function calculate_weighted_average(**grades) that:
- Accepts subjects as keys and (score, weight) tuples as values
- Calculates weighted average of all grades
- Returns the weighted average rounded to 2 decimal places
def calculate_weighted_average(**grades) -> float:
"""
Calculate weighted average from keyword arguments.
Example:
calculate_weighted_average(
math=(85, 0.3), # score=85, weight=30%
science=(90, 0.3), # score=90, weight=30%
english=(80, 0.4) # score=80, weight=40%
) -> 84.5
"""
pass
Statistics Functions with *args
Create these statistical functions that accept variable arguments:
mean(*numbers)- Returns arithmetic meanmedian(*numbers)- Returns median valuemode(*numbers)- Returns most frequent value (first if tie)
def mean(*numbers) -> float:
"""Calculate the arithmetic mean of numbers."""
pass
def median(*numbers) -> float:
"""Calculate the median of numbers."""
pass
def mode(*numbers):
"""Return the most frequent number. First value if tie."""
pass
Lambda Functions
Create these operations using lambda expressions:
square- Lambda that squares a numbercube- Lambda that cubes a numberis_even- Lambda that returns True if evenis_positive- Lambda that returns True if positive
# Define these as lambda expressions
square = lambda x: ...
cube = lambda x: ...
is_even = lambda x: ...
is_positive = lambda x: ...
Higher-Order Function
Create a function apply_operation(numbers, operation) that:
- Takes a list of numbers and a function as arguments
- Applies the operation to each number
- Returns a new list with results
def apply_operation(numbers: list, operation) -> list:
"""
Apply an operation to each number in the list.
Example:
apply_operation([1, 2, 3], square) -> [1, 4, 9]
apply_operation([1, 2, 3], lambda x: x * 10) -> [10, 20, 30]
"""
pass
Closure: Counter Factory
Create a function make_counter(start=0) that:
- Returns a closure function
- Each call to the closure increments and returns the count
- Demonstrates nonlocal variable usage
def make_counter(start: int = 0):
"""
Create a counter closure.
Example:
counter = make_counter(10)
counter() -> 11
counter() -> 12
counter() -> 13
"""
pass
Closure: Multiplier Factory
Create a function make_multiplier(factor) that:
- Returns a function that multiplies its input by the factor
- Demonstrates closure capturing outer variable
def make_multiplier(factor: float):
"""
Create a multiplier closure.
Example:
double = make_multiplier(2)
triple = make_multiplier(3)
double(5) -> 10
triple(5) -> 15
"""
pass
Decorator: Logging
Create a decorator @log_call that:
- Prints the function name and arguments before execution
- Prints the return value after execution
- Works with any function signature
def log_call(func):
"""
Decorator that logs function calls.
Example output:
Calling: add(5, 3)
Returned: 8
"""
pass
@log_call
def add_logged(a, b):
return a + b
Decorator: Validation
Create a decorator @validate_positive that:
- Checks if all numeric arguments are positive
- Raises
ValueErrorif any argument is negative or zero - Passes through if all arguments are positive
def validate_positive(func):
"""
Decorator that validates all numeric arguments are positive.
Raises ValueError if any argument is <= 0.
"""
pass
@validate_positive
def calculate_area(length, width):
return length * width
Recursive: Factorial
Create a recursive function factorial(n) that:
- Calculates factorial using recursion
- Handles base case (0! = 1, 1! = 1)
- Raises ValueError for negative numbers
def factorial(n: int) -> int:
"""
Calculate factorial recursively.
Example:
factorial(5) -> 120 # 5*4*3*2*1
factorial(0) -> 1
"""
pass
Recursive: Fibonacci
Create a recursive function fibonacci(n) that:
- Returns the nth Fibonacci number
- fibonacci(0) = 0, fibonacci(1) = 1
- Each subsequent number is sum of previous two
def fibonacci(n: int) -> int:
"""
Calculate nth Fibonacci number recursively.
Sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, ...
Example:
fibonacci(6) -> 8
fibonacci(10) -> 55
"""
pass
Recursive: Sum of Digits
Create a recursive function sum_digits(n) that:
- Calculates sum of all digits in a number
- Works with positive integers only
- Uses recursion (not string conversion)
def sum_digits(n: int) -> int:
"""
Calculate sum of digits recursively.
Example:
sum_digits(12345) -> 15 # 1+2+3+4+5
sum_digits(999) -> 27 # 9+9+9
"""
pass
Main Function with Demonstrations
Create a main() function that:
- Demonstrates all implemented functions
- Prints clear output for each demonstration
- Uses
if __name__ == "__main__":pattern
def main():
"""Demonstrate all calculator library functions."""
print("=" * 50)
print("CALCULATOR LIBRARY DEMONSTRATION")
print("=" * 50)
# Basic Arithmetic
print("\n1. Basic Arithmetic:")
print(f" add(10, 5) = {add(10, 5)}")
print(f" subtract(10, 5) = {subtract(10, 5)}")
print(f" multiply(10, 5) = {multiply(10, 5)}")
print(f" divide(10, 5) = {divide(10, 5)}")
# ... demonstrate all other functions
print("\n" + "=" * 50)
print("All demonstrations complete!")
if __name__ == "__main__":
main()
Submission
Create a public GitHub repository with the exact name shown below:
Required Repository Name
python-calculator-library
Required Files
python-calculator-library/
├── calculator_library.py # Your main Python file with ALL 15 requirements
├── test_calculator.py # Unit tests for your functions (at least 10 tests)
├── output.txt # Output from running main() function
└── README.md # REQUIRED - see contents below
README.md Must Include:
- Your full name and submission date
- Brief description of each function category implemented
- Any challenges faced and how you solved them
- Instructions to run the library and tests
Do Include
- All 15 requirements implemented
- Docstrings for every function
- Type hints where appropriate
- Clear, organized code structure
- Unit tests in test_calculator.py
- README.md with all required sections
Do Not Include
- External libraries (ONLY math module allowed)
- Any .pyc or __pycache__ files (use .gitignore)
- Virtual environment folders
- Code that doesn't run without errors
- Copied code from the internet
Enter your GitHub username - we'll verify your repository automatically
Grading Rubric
Your assignment will be graded on the following criteria:
| Criteria | Points | Description |
|---|---|---|
| Basic Functions (1-2) | 15 | Correct implementation of basic arithmetic and power functions with docstrings |
| Variable Arguments (3-5) | 25 | Proper use of *args, **kwargs, and statistical calculations |
| Lambda Functions (6-7) | 20 | Correct lambda definitions and higher-order function usage |
| Closures (8-9) | 20 | Proper closure implementations with nonlocal variables |
| Decorators (10-11) | 25 | Working decorators for logging and validation |
| Recursion (12-14) | 25 | Correct recursive implementations with proper base cases |
| Code Quality & Testing | 20 | Docstrings, type hints, unit tests, and clean organization |
| Total | 150 |
Ready to Submit?
Make sure you have completed all requirements and reviewed the grading rubric above.
Submit Your AssignmentWhat You Will Practice
Function Basics (5.1)
Defining functions with parameters, default values, type hints, docstrings, and return statements
*args and **kwargs (5.2)
Variable positional arguments, keyword arguments, unpacking operators, and flexible function signatures
Lambda & Higher-Order Functions (5.2)
Anonymous functions, functions as arguments, map/filter/reduce patterns, functional programming
Closures & Decorators (5.2-5.3)
Factory functions, closures, nonlocal scope, decorator patterns, function wrapping
Pro Tips
Function Best Practices
- Keep functions small and focused (single responsibility)
- Use descriptive names that indicate what the function does
- Always include docstrings with examples
- Use type hints for better code clarity
Recursion Tips
- Always define a clear base case first
- Ensure recursive calls move toward the base case
- Consider stack overflow for large inputs
- Test with edge cases (0, 1, negative)
Decorator Tips
- Use
functools.wrapsto preserve function metadata - Handle *args and **kwargs in wrapper functions
- Return the result from the wrapped function
- Test decorators with different function signatures
Common Mistakes
- Forgetting to return a value from functions
- Mutable default arguments (use None instead)
- Missing base case in recursion (infinite loop)
- Not handling edge cases in calculations