【问题标题】:How to get the runtime type of a class generic type by using a decorator?如何使用装饰器获取类泛型类型的运行时类型?
【发布时间】:2020-09-27 12:39:36
【问题描述】:

我有一个通用类,比如

from typing import Generic, TypeVar, List

T = TypeVar('T')

@my_decorator
class Foo(Generic[T]):
    pass

f: Foo[int] = Foo()
g: Foo[List[float]] = Foo()

有没有一种简洁的方法可以在装饰器中获取构造函数调用Foo[int], Foo[List[float]] 的类型注释?我想在运行时做一些类型检查。

我可以通过装饰器访问Foo 的构造函数调用,我什至可以通过使用inspect.stack()inspect.getsource(my_frame) 以非常不雅的方式获得构造函数调用的代码行f: Foo[int] = Foo()。然后我可以通过一些字符串操作得到Foo[int]

除了这是一种非常肮脏的方法之外,它只能将类型作为字符串获取。但我需要实际的类型。在本例中,我可以使用eval() 来“解析”字符串并将其转换为类型。但这不适用于自定义类,例如以下示例:

from typing import Generic, TypeVar, List

T = TypeVar('T')

class Bar:
    pass

@my_decorator
class Foo(Generic[T]):
    pass

h: Foo[List[Bar]] = Foo()

在这种情况下,我不能使用eval(),因为我不知道如何才能获得正确的context。 我喜欢得到类似my_file.Foo[typing.List[my_file.Bar]] 这样的东西,它可以让我在运行时进行类型检查。

那么有什么干净的方法吗?或者至少有一种(肮脏的)方法可以为eval()“解析”字符串获得正确的上下文?

【问题讨论】:

标签: python python-3.x generics annotations typing


【解决方案1】:

TL;DR:这不可能在 __init__ 中获取泛型的运行时类型,但我们可以在 CPython 实现中足够接近

  1. 要仅使用类中的装饰器来处理此问题,您应该将调用更改为h = Foo[List[Bar]](),以便装饰器可以独立于保存返回对象的变量访问类型提示。

  2. This answer 表示泛型类的类实例在初始化后 具有可用的__orig_class__ 属性。该属性在 init 之后设置(参见source code)。

因此,如果我们编写一个类装饰器,装饰器应该修改源类,以便在运行时设置__orig_class__ 属性时基本监听。不过,这在很大程度上依赖于未记录的实现细节,并且在 Python 的未来版本或其他实现中可能不会以同样的方式工作。

def my_decorator(cls):
    orig_bases = cls.__orig_bases__
    genericType = orig_bases[0]
    class cls2(cls, genericType):
        def __init__(self, *args, **kwargs):
            super(cls2, self).__init__(*args, *kwargs)
        def __setattr__(self, name, value):
            object.__setattr__(self, name, value)
            if name == "__orig_class__":
                print("Runtime generic type is " + str(get_args(self.__orig_class__)))
    cls2.__orig_bases__ = orig_bases
    return cls2

然后:

>>> h = Foo[List[Bar]]()
Runtime generic type is (typing.List[__main__.Bar],)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多