我想使用新的 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
)。
如果您阅读
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'>,)
演示这里