自定义类型提示注解

Rod*_*ins 9 python type-hinting code-completion

我刚刚为 Python编写了一个简单的@autowired装饰器,它根据类型注释实例化类。

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

@autowired
def foo(bla, *, dep: lazy(MyClass)):
   ...
Run Code Online (Sandbox Code Playgroud)

这工作得很好,在幕后这个lazy函数只是返回一个函数,该函数返回实际类型并且将lazy_init属性设置为True。也这并没有打破的IDE(例如,PyCharm)代码完成功能。

但我想启用可下标Lazy类型 use 而不是lazy函数的使用。

像这样:

@autowired
def foo(bla, *, dep: Lazy[MyClass]):
   ...
Run Code Online (Sandbox Code Playgroud)

这会表现得非常像Typing.Union。而当我能够实现标化的类型,Ides的代码完成功能将形同虚设,因为它会出现在为属性的建议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
Run Code Online (Sandbox Code Playgroud)

我尝试重新定义Lazy.__dict__为转发到下标类型的属性,__dict__但这似乎对 PyCharm 的代码完成功能没有影响。

我坚信我想要实现的目标是可能的,因为Typing.Union可以很好地与 IDE 的代码完成配合使用。我一直试图破译typing.Union的源代码中的内容,使其在代码完成功能方面表现良好,但到目前为止还没有成功。

Mar*_*ers 10

为了使Container[Type]表示法起作用,您需要创建一个用户定义的泛型类型

from typing import TypeVar, Generic

T = TypeVar('T')

class Lazy(Generic[T]):
    pass
Run Code Online (Sandbox Code Playgroud)

然后你使用

def foo(bla, *, dep: Lazy[MyClass]):
Run Code Online (Sandbox Code Playgroud)

并且Lazy被视为一个容纳类的容器。

注意:这仍然意味着 IDE 将其dep视为类型的对象LazyLazy这里是一个容器类型,持有一个类型的对象MyClass。您的 IDE 不会自动完成该MyClass类型,您不能那样使用它。

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

>>> a = Lazy[str]
>>> issubclass(a, Lazy)
True
>>> a.__args__
(<class 'str'>,)
Run Code Online (Sandbox Code Playgroud)

如果您只想在运行时访问类型注释但懒惰地解析名称,则可以只支持字符串值:

def foo(bla, *, dep: 'MyClass'):
Run Code Online (Sandbox Code Playgroud)

这是有效的类型注释,以及你的装饰器可以通过在运行时将名称解析typing.get_type_hints()功能(在延期的时间,而不是在装修时),或在你的包裹串lazy()在装修时可调用。

如果lazy()旨在将类型标记为与其他类型提示不同的处理方式,那么您正在尝试使用其他含义重载类型提示注释,并且类型提示根本不支持此类用例,并且使用 Lazy[...]包含不能让它起作用。

  • @RodrigoMartins:你的*代码*可以以不同的方式对待“Lazy”,但是像你的IDE那样的类型暗示消费者不会。`Union` 在类型提示规范中具有特定含义,因此它确实被 IDE 视为代理。它这样做是因为规范告诉它这样做。然而,规范中没有规定支持用户定义的代理。 (2认同)