You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python @autowired装饰器延迟初始化实现及优化需求问询

优化你的Python @Autowired与Lazy初始化实现

Nice work getting that @autowired decorator and lazy initialization function up and running—keeping IDE code completion intact is a huge win, so kudos on that detail! Below are practical, actionable optimization directions to polish your implementation, covering type safety, flexibility, debuggability, and extensibility:

1. Boost Type Safety & Generic Support

Your current lazy function likely handles basic types and string references well, but it might struggle with generic type annotations (like list[User] or dict[str, Order]). Extend it to resolve generics, cache resolved types, and preserve type hints for IDEs:

from typing import Type, TypeVar, Generic, get_args, get_origin
import importlib

T = TypeVar('T')

class Lazy(Generic[T]):
    def __init__(self, type_ref: Type[T] | str):
        self._type_ref = type_ref
        self._resolved_type: Type[T] | None = None
        self._instance: T | None = None

    def _resolve_type(self) -> Type[T]:
        if isinstance(self._type_ref, str):
            # Cache resolved types to avoid repeated imports
            if not self._resolved_type:
                try:
                    module_name, class_name = self._type_ref.rsplit('.', 1)
                    module = importlib.import_module(module_name)
                    self._resolved_type = getattr(module, class_name)
                except (ValueError, ImportError, AttributeError) as e:
                    raise ValueError(f"Failed to resolve type '{self._type_ref}': {str(e)}") from e
        else:
            self._resolved_type = self._type_ref
        
        # Handle generics (extract the actual type if needed)
        origin = get_origin(self._resolved_type)
        if origin is not None:
            self._resolved_type = get_args(self._resolved_type)[0]
        return self._resolved_type

    def __call__(self) -> T:
        if self._instance is None:
            type_ = self._resolve_type()
            self._instance = type_()
        return self._instance

def lazy(type_annotation: Type[T] | str) -> Lazy[T]:
    return Lazy(type_annotation)

This implementation uses Generic[T] to preserve IDE type inference, caches resolved types for better performance, and adds clear error handling for invalid type references.

2. Configurable Instance Scopes

Add support for different instance lifecycles (singleton, transient) to make your tool more flexible for different use cases:

from enum import Enum

class Scope(Enum):
    SINGLETON = "singleton"  # Reuse one instance (default)
    TRANSIENT = "transient"  # Create a new instance every time

class Lazy(Generic[T]):
    def __init__(self, type_ref: Type[T] | str, scope: Scope = Scope.SINGLETON, **init_kwargs):
        self._type_ref = type_ref
        self._scope = scope
        self._init_kwargs = init_kwargs
        self._resolved_type: Type[T] | None = None
        self._instance: T | None = None

    def __call__(self) -> T:
        type_ = self._resolve_type()
        if self._scope == Scope.SINGLETON:
            if self._instance is None:
                self._instance = type_(**self._init_kwargs)
            return self._instance
        else:
            return type_(**self._init_kwargs)

def lazy(type_annotation: Type[T] | str, scope: Scope = Scope.SINGLETON, **init_kwargs) -> Lazy[T]:
    return Lazy(type_annotation, scope, **init_kwargs)

Now users can choose whether to reuse a single instance or create new ones on demand, and even pass initialization arguments directly to lazy.

3. IDE-Friendly Explicit Annotations

Use typing.Annotated to mark lazy-injected fields, making intent clearer for both IDEs and static type checkers like mypy:

from typing import Annotated
import inspect

# Define a marker for lazy initialization
LazyInit = object()

def autowired(cls):
    # Handle class attributes
    for name, annotation in cls.__annotations__.items():
        if get_origin(annotation) is Annotated:
            type_, *metadata = get_args(annotation)
            if LazyInit in metadata:
                setattr(cls, name, lazy(type_)())
    
    # Handle constructor parameters
    original_init = cls.__init__
    def new_init(self, *args, **kwargs):
        sig = inspect.signature(original_init)
        for param_name, param in sig.parameters.items():
            if get_origin(param.annotation) is Annotated:
                type_, *metadata = get_args(param.annotation)
                if LazyInit in metadata and param_name not in kwargs:
                    kwargs[param_name] = lazy(type_)()
        original_init(self, *args, **kwargs)
    
    cls.__init__ = new_init
    return cls

# Usage example
class Service:
    def process(self):
        pass

@autowired
class App:
    # Lazy-injected class attribute
    service: Annotated[Service, LazyInit]

    def __init__(self, db: Annotated[Database, LazyInit]):
        # Lazy-injected constructor parameter
        self.db = db

This makes your code more self-documenting and ensures tools like PyCharm or mypy correctly recognize the field types.

4. Enhanced Debugging & Error Handling

Add logging and better error messages to simplify debugging dependency issues:

import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

class Lazy(Generic[T]):
    # ... (previous code)

    def __call__(self) -> T:
        if self._instance is None:
            type_ = self._resolve_type()
            logger.debug(f"Lazy initializing {type_.__name__} (scope: {self._scope.value})")
            try:
                self._instance = type_(**self._init_kwargs)
            except Exception as e:
                raise RuntimeError(f"Failed to initialize {type_.__name__}: {str(e)}") from e
        return self._instance

Now you’ll get clear logs when instances are initialized, and more context when initialization fails.

These optimizations can be rolled out incrementally based on your needs—start with type safety and scopes if you’re building a general-purpose tool, or focus on constructor injection if you’re targeting complex applications.

内容的提问来源于stack exchange,提问作者Rodrigo Oliveira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:32:34