如果您阅读typing.py
的源代码,您会看到Generic
的类型参数存储为Base Class _BaseGenericAlias
的__args__
属性.请注意,这是一个未记录的实现细节.
然后,当该方法用classmethod
子类装饰(标记了特殊属性的函数)时,我们只需要修补其代理属性getter _BaseGenericAlias.__getattr__
,以将额外的关键字参数注入到具有包装器函数的方法调用中:
class TypeArgsClassMethod(classmethod):
def __get__(self, obj, obj_type=None):
method = super().__get__(obj, obj_type)
method.__func__._inject_type_args = True
return method
def __getattr__(self, name):
if hasattr(obj := orig_getattr(self, name), '_inject_type_args'):
@wraps(obj)
def wrapper(*args, **kwargs):
return obj(*args, __type_args__=self.__args__, **kwargs)
return wrapper
return obj
if _BaseGenericAlias.__getattr__ is not __getattr__:
orig_getattr = _BaseGenericAlias.__getattr__
_BaseGenericAlias.__getattr__ = __getattr__
以便:
class MyClass[T]:
kind: type[T]
@TypeArgsClassMethod
def make_class(cls, __type_args__) -> "MyClass[T]":
print(__type_args__)
return cls()
MyClass[int].make_class()
输出:
(<class 'int'>,)
演示 here
或者,您可以将自定义类方法重新绑定到Generic
类型,以便类方法的第一个参数将成为Generic
类型.内置types.MethodType
不允许更新__self__
以进行重新绑定,因此您必须定义Python-equivalent version:
from functools import update_wrapper
from typing import _BaseGenericAlias
class MethodType:
def __init__(self, func, obj):
self.__func__ = func
self.__self__ = obj
def __call__(self, *args, **kwargs):
func = self.__func__
obj = self.__self__
return func(obj, *args, **kwargs)
class GenericClassMethod:
def __init__(self, f):
self.f = f
update_wrapper(self, f)
def __get__(self, obj, cls=None):
if cls is None:
cls = type(obj)
method = MethodType(self.f, cls)
method._generic_classmethod = True
return method
def __getattr__(self, name):
if hasattr(obj := orig_getattr(self, name), '_generic_classmethod'):
obj.__self__ = self
return obj
if _BaseGenericAlias.__getattr__ is not __getattr__:
orig_getattr = _BaseGenericAlias.__getattr__
_BaseGenericAlias.__getattr__ = __getattr__
以便您可以访问类型参数的__args__
属性,以及原始类的__origin__
属性:
class MyClass[T]:
kind: type[T]
@GenericClassMethod
def make_class(cls) -> "MyClass[T]":
print(cls)
print(cls.__origin__)
print(cls.__args__)
MyClass[int].make_class()
This 输出:
__main__.MyClass[int]
<class '__main__.MyClass'>
(<class 'int'>,)
演示 here