@autowired
装饰器,它根据类型注释实例化类。
为了启用类的延迟初始化,该包提供了一个
lazy(type_annotation: (Type, str))
函数,以便调用者可以像这样使用它:
@autowired
def foo(bla, *, dep: lazy(MyClass)):
...
这工作得很好,在幕后这个
lazy
函数只返回一个返回实际类型的函数,并且将 lazy_init 属性设置为 True
。此外,这不会破坏 IDE(例如 PyCharm)的代码完成功能。
Lazy
类型而不是 lazy
函数。像这样:
@autowired
def foo(bla, *, dep: Lazy[MyClass]):
...
这与 typing.Union 非常相似。 虽然我能够实现可订阅类型,但 IDE 的代码完成功能将变得毫无用处,因为它将为
Lazy
类中的属性提供建议,而不是 MyClass
。
我一直在使用这段代码:
class LazyMetaclass(type):
def __getitem__(lazy_type, type_annotation):
return lazy_type(type_annotation)
class Lazy(metaclass=LazyMetaclass):
def __init__(self, type_annotation):
self.type_annotation = type_annotation
我尝试将
Lazy.__dict__
重新定义为属性以转发到下标类型的 __dict__
但这似乎对 PyCharm 的代码完成功能没有影响。
我坚信我想要实现的目标是可能的,因为 typing.Union 与 IDE 的代码补全配合得很好。我一直在尝试破译 typing.Union 的源代码中的内容使其在代码完成功能方面表现良好,但到目前为止尚未成功。
为了使
Container[Type]
表示法发挥作用,您需要创建一个 用户定义的泛型类型:
from typing import TypeVar, Generic
T = TypeVar('T')
class Lazy(Generic[T]):
pass
然后你使用
def foo(bla, *, dep: Lazy[MyClass]):
和
Lazy
被视为容纳类的容器。
注意:这 仍然意味着 IDE 将
dep
视为 Lazy
类型的对象。 Lazy
在这里是一个容器类型,holding是MyClass
类型的对象。您的 IDE 不会自动完成 MyClass
类型,您不能那样使用它。
该表示法也不会创建
Lazy
类的实例;它会创建(私有)typing._GenericAlias
类的实例。该实例有一个属性 __args__
让您内省订阅参数:
>>> a = Lazy[str]
>>> type(a)
<class 'typing._GenericAlias'>
>>> a.__args__
(<class 'str'>,)
typing.get_args()
函数;此函数处理一些具有特定类型提示对象的边缘情况,其中访问 __args__
会导致令人惊讶的结果:
>>> from typing import get_args
>>> get_args(Lazy[str])
(<class 'str'>,)
如果您想要的只是在运行时访问类型注释,但延迟解析名称,您可以只支持字符串值:
def foo(bla, *, dep: 'MyClass'):
这是有效的类型注释,并且您的装饰器可以在运行时通过使用typing.get_type_hints()
函数(在延迟时间,而不是在装饰时间)解析名称,或者通过将字符串包装在您的
lazy()
可调用中装修时间。如果
lazy()
旨在标记要与其他类型提示区别对待的类型,那么您正在尝试使用其他含义重载类型提示注释,您希望使用
Annotated[type_hint, metadata]
注释附加额外的元数据供第三方使用的类型提示。 例如将
Lazy
注释附加到类型提示将如下所示:
from typing import Annotated
def foo(bla, *, dep: Annotated[MyClass, Lazy]):
Annotated[type_hint, ...]
看起来像
type_hint
来输入跳棋。尝试从运行时代码访问此类提示时使用
get_type_hints(..., include_extras=True)
,否则
Annotated
对象将被剥离。然后,当内省
Annotated
对象时,查看
__metadata__
属性来内省额外的元数据。