Python 3.12 中的 Generic[T] 类方法隐式类型检索

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

我想使用新的 Python 3.12 泛型类型签名语法来了解该类的 classmethod 中即将实例化的类的类型。

例如,我想在这个例子中打印

T
的具体类型:

class MyClass[T]:
    kind: type[T]

    ...

    @classmethod
    def make_class[T](cls) -> "MyClass[T]":
        print("I am type {T}!")
        return cls()

我已经使用了以下 StackOverflow 问题中的建议来具体化

__new__
__init__
中的类型,但我还没有找到一种聪明的方法来在静态或类方法中执行此操作(最好是类方法)。

我的目标是拥有以下 API:

>>> MyClass[int].make_class()
"I am type int!"

或者这个 API(我认为在语法上还不可能):

>>> MyClass.make_class[int]()
"I am type int!"

无论哪种情况,返回的实例都会将

int
绑定到类变量,以便我稍后可以使用它。

MyClass[int].make_class().kind is int == True

我对“黑客”持开放态度(包括大量使用

inspect
)。

python generics python-typing
1个回答
0
投票

如果您阅读

typing.py
的源代码,您会发现
Generic
的类型参数存储为基类 __args__
_BaseGenericAlias
 属性。

然后我们只需要修补

_BaseGenericAlias.__getattr__
,这样如果原始方法返回的对象是一个类方法(使用
thisanswer
中的isclassmethod函数标识),它将返回一个注入了
__args__
的包装函数作为调用时的附加关键字参数:

from functools import wraps
from typing import get_args, _BaseGenericAlias

def __getattr__(self, name):
    if isclassmethod(obj := orig_getattr(self, name)):
        @wraps(obj)
        def wrapper(*args, **kwargs):
            return obj(*args, __type_args__=self.__args__, **kwargs)
        return wrapper
    return obj

orig_getattr = _BaseGenericAlias.__getattr__
_BaseGenericAlias.__getattr__ = __getattr__

class MyClass[T]:
    kind: type[T]

    @classmethod
    def make_class(cls, __type_args__) -> "MyClass[T]":
        print(__type_args__)
        return cls()

MyClass[int].make_class()

输出:

(<class 'int'>,)

演示这里

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