自定义类型提示注释

问题描述 投票:0回答:1

我刚刚为 Python 编写了一个简单的

@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 的源代码中的内容使其在代码完成功能方面表现良好,但到目前为止尚未成功。

python code-completion type-hinting
1个回答
16
投票

为了使

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__
 属性来内省额外的元数据。

© www.soinside.com 2019 - 2024. All rights reserved.