# Leveraging Python Decorators for Elegant Type Inference Python offers multiple approaches for creating flexible, type-safe code. While class inheritance is a common method, decorators provide a compelling alternative because of automatic type inference. Let's compare class inheritance and decorators for type inference using a grade calculator example. This calculator converts numerical scores to either numeric grades (0-10 scale) or letter grades (A-F). ## Class Inheritance Approach This method uses a base class with generic types and subclasses for specific implementations: ```python from typing import Generic, TypeVar T = TypeVar('T') U = TypeVar('U') class GradeCalculator(Generic[T, U]): def calculate_grade(self, score: T) -> U: raise NotImplementedError def describe_grade(self, grade: U) -> str: return f"The calculated grade is: {grade}" class NumericGradeCalculator(GradeCalculator[float, float]): def calculate_grade(self, score: float) -> float: return round(score / 10, 1) class LetterGradeCalculator(GradeCalculator[int, str]): def calculate_grade(self, score: int) -> str: if score >= 90: return "A" elif score >= 80: return "B" elif score >= 70: return "C" elif score >= 60: return "D" else: return "F" # Usage numeric_calc = NumericGradeCalculator() letter_calc = LetterGradeCalculator() print(numeric_calc.describe_grade(numeric_calc.calculate_grade(85.5))) print(letter_calc.describe_grade(letter_calc.calculate_grade(85))) ``` ## Decorator Approach This method uses a class decorator to wrap functions with additional functionality: ```python from typing import Callable, TypeVar, Generic T = TypeVar('T') U = TypeVar('U') class GradeCalculator(Generic[T, U]): def __init__(self, calculate_grade: Callable[[T], U]): self.calculate_grade = calculate_grade def describe_grade(self, grade: U) -> str: return f"The calculated grade is: {grade}" @GradeCalculator def numeric_grade_calculator(score: float) -> float: return round(score / 10, 1) @GradeCalculator def letter_grade_calculator(score: int) -> str: if score >= 90: return "A" elif score >= 80: return "B" elif score >= 70: return "C" elif score >= 60: return "D" else: return "F" # Usage print(numeric_grade_calculator.describe_grade(numeric_grade_calculator.calculate_grade(85.5))) print(letter_grade_calculator.describe_grade(letter_grade_calculator.calculate_grade(85))) ``` ## Comparison Both approaches have their merits: 1. **Inheritance**: - Suitable for complex scenarios with multiple methods - Allows for extensive hierarchies and method overriding - Explicit about types and relationships 2. **Decorators**: - Provide automatic type inference - Result in cleaner, more concise code for simple cases - Easy to apply to existing functions ## Conclusion For simple cases, decorators offer cleaner code through automatic type inference. For complex scenarios, traditional class-based approaches may be more suitable. Choose the method that best fits your specific use case and complexity requirements.