自定义类型提示注释

时间:2018-02-10 16:16:26

标签: python code-completion type-hinting

我刚刚为Python编写了一个简单的@autowired decorator,它基于类型注释实例化了类。

要启用类的延迟初始化,该包提供lazy(type_annotation: (Type, str))函数,以便调用者可以像这样使用它:

@autowired
def foo(bla, *, dep: lazy(MyClass)):
   ...

这非常有效,在这个问题下,这个lazy函数只返回一个返回实际类型的函数,并且 lazy_init 属性设置为True。这也是打破IDE(例如,PyCharm)代码完成功能。

But I want to enable the use of a subscriptable Lazy type use instead of the lazy function.

像这样:

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

1 个答案:

答案 0 :(得分:3)

要使Container[Type]符号起作用,您需要创建user-defined generic 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是此处的容器类型,持有类型为MyClass的对象。您的IDE不会自动完成MyClass类型,您不能以这种方式使用它。

符号也不会创建Lazy类的实例;它通过GenericMeta元类创建一个子类。子类有一个特殊的属性__args__,可以让你自省订阅参数:

>>> a = Lazy[str]
>>> issubclass(a, Lazy)
True
>>> a.__args__
(<class 'str'>,)

如果你想要的只是在运行时进入类型注释但是懒惰地解析名称,你可以只支持一个字符串值:

def foo(bla, *, dep: 'MyClass'):

这是有效的类型注释,你的装饰者可以在运行时使用typing.get_type_hints() function(在延迟时间,而不是在装饰时)或通过包装字符串来解析名称你的lazy()可以在装修时调用。

如果lazy()用于标记要与其他类型提示区别对待的类型,那么您尝试使用其他一些含义重载类型提示注释,并且类型提示根本不支持此类用例,并且使用包含Lazy[...]的用例无法使其正常工作。