Skip to content

Latest commit

 

History

History
148 lines (126 loc) · 5.83 KB

lazy_load_class.md

File metadata and controls

148 lines (126 loc) · 5.83 KB

Lazy-Loaded Python Class Template

Description

This template provides a foundational structure for a Python class that uses lazy-loading to avoid circular imports and unnecessary initialization. The class also includes functionality for logging, dynamic dependency injection, and data validation using Pydantic.

Code Example

from __future__ import annotations
from typing import TYPE_CHECKING, Optional, List, Dict, Any
from pydantic import BaseModel, ValidationError
import logging

# Conditional imports to avoid circular dependencies
if TYPE_CHECKING:
    from your_module_name_here import DependentClass1, DependentClass2  # Replace with actual class/module names

# Pydantic BaseModel example for data validation
class ExampleModel(BaseModel):
    id: str
    value: int
    description: Optional[str] = None

class LazyLoadedClass:
    """
    A template class with lazy-loaded dependencies to avoid circular imports and unnecessary initialization.

    Attributes:
        param1 (str): A parameter to demonstrate class initialization.
        param2 (int): Another parameter to demonstrate class initialization.
        _dependent_class1 (Optional[DependentClass1]): Lazy-loaded instance of DependentClass1.
        _dependent_class2 (Optional[DependentClass2]): Lazy-loaded instance of DependentClass2.
        _logger (Optional[logging.Logger]): Lazy-loaded logger instance.
    """
    
    def __init__(self, param1: str, param2: int):
        self.param1 = param1
        self.param2 = param2
        self._dependent_class1: Optional[DependentClass1] = None
        self._dependent_class2: Optional[DependentClass2] = None
        self._logger: Optional[logging.Logger] = None

    @property
    def logger(self) -> logging.Logger:
        """Lazy-load the logger."""
        if self._logger is None:
            self._logger = logging.getLogger(self.__class__.__name__)
            self._logger.setLevel(logging.INFO)  # Default to INFO level, adjust as needed
            handler = logging.StreamHandler()
            handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
            self._logger.addHandler(handler)
            self._logger.debug("Logger initialized for %s", self.__class__.__name__)
        return self._logger

    @property
    def dependent_class1(self) -> DependentClass1:
        """Lazy-load DependentClass1."""
        if self._dependent_class1 is None:
            from your_module_name_here import DependentClass1  # Replace with actual module
            self._dependent_class1 = DependentClass1()
            self.logger.debug("DependentClass1 initialized")
        return self._dependent_class1

    @property
    def dependent_class2(self) -> DependentClass2:
        """Lazy-load DependentClass2."""
        if self._dependent_class2 is None:
            from your_module_name_here import DependentClass2  # Replace with actual module
            self._dependent_class2 = DependentClass2()
            self.logger.debug("DependentClass2 initialized")
        return self._dependent_class2

    def some_method(self) -> None:
        """A method demonstrating the usage of lazy-loaded dependencies."""
        try:
            result1 = self.dependent_class1.some_method()
            result2 = self.dependent_class2.another_method()
            self.logger.info("Successfully called methods on lazy-loaded classes")
        except Exception as e:
            self.logger.error("Error in some_method: %s", str(e))
            raise

    def to_dict(self) -> Dict[str, Any]:
        """Convert the class attributes to a dictionary."""
        try:
            return {
                "param1": self.param1,
                "param2": self.param2,
                "dependent_class1": self.dependent_class1.to_dict(),
                "dependent_class2": self.dependent_class2.to_dict(),
            }
        except Exception as e:
            self.logger.error("Error converting to dict: %s", str(e))
            raise

    def add_dependent_class(self, class_type: str, class_instance: Any) -> None:
        """
        Dynamically add a dependent class instance to the LazyLoadedClass.

        Args:
            class_type (str): The name of the class being added (e.g., 'dependent_class1').
            class_instance (Any): The instance of the class being added.

        Raises:
            ValueError: If the class_type is not recognized.
        """
        if class_type == 'dependent_class1':
            self._dependent_class1 = class_instance
        elif class_type == 'dependent_class2':
            self._dependent_class2 = class_instance
        else:
            self.logger.error("Invalid class_type provided: %s", class_type)
            raise ValueError(f"Invalid class_type: {class_type}")

    def validate_model(self, data: Dict[str, Any]) -> ExampleModel:
        """
        Validate input data against the ExampleModel schema.

        Args:
            data (Dict[str, Any]): The data to be validated.

        Returns:
            ExampleModel: The validated model instance.

        Raises:
            ValidationError: If the data is not valid according to ExampleModel.
        """
        try:
            model = ExampleModel(**data)
            self.logger.info("Data successfully validated against ExampleModel")
            return model
        except ValidationError as e:
            self.logger.error("Validation error: %s", str(e))
            raise

# Usage example demonstrating lazy loading and data validation
if __name__ == "__main__":
    try:
        example = LazyLoadedClass(param1="example", param2=123)
        example.some_method()
        validated_model = example.validate_model({"id": "1", "value": 10})
        print(validated_model.dict())
        example_dict = example.to_dict()
        print(example_dict)
    except Exception as e:
        logging.error("Error in usage example: %s", str(e))