目标是使以下伪代码在Python 3.7+中有效,并让静态分析工具理解该伪代码。
class VariadicType(MaybeASpecialBaseClass, metaclass=MaybeASpecialMetaClass):
@classmethod
def method(cls)->Union[???]:
pass # some irrelevant code
assert(VariadicType[Type1, Type2, Type3, Type4].method.__annotations__["return"] == Union[Type1, Type2, Type3, Type4])
assert(VariadicType[Type1, Type2, Type3, Type4, Type5].method.__annotations__["return"] == Union[Type1, Type2, Type3, Type4, Type5])
是否可以支持某种
class VariadicType(Generic[...])
,但随后获取所有传递的泛型类型?我正在考虑使用C#方法
class VariadicType(Generic[T1]):
...
class VariadicType(Generic[T1, T2]):
...
class VariadicType(Generic[T1, T2, T3]):
...
class VariadicType(Generic[T1, T2, T3, T4]):
...
class VariadicType(Generic[T1, T2, T3, T4, T5]):
...
但是它不是有效的代码-
VariadicType
只能定义一次。PS。代码的无关部分应检查
__annotations__["return"]
并相应地返回结果。它正在应用mixin。如果返回类型不是所有应用的mixin的并集,则静态分析会抱怨缺少字段和方法。不得已的方法是使用非提示代码,其中将类型指定为方法参数,但返回类型为Any
。 最佳答案
我已经遇到了这个问题,所以也许我可以提出一些建议。
问题
假设我们有下一个类定义:
T = TypeVar('T')
S = TypeVar('S')
class VaradicType(Generic[T, S]):
pass
问题是
VaradicType[T, S]
调用VaradicType.__class_getitem__((T, S))
会返回类_GenericAlias
的对象。然后,如果执行
cls = VaradicType[int, float]
,则可以使用以下命令内省用作索引的参数:cls.__args__
。但是,如果实例化
obj = cls()
之类的对象,则不能执行obj.__class__.__args__
。这是因为
_GenericAlias
实现了__call__
方法,该方法直接返回VaradicType
对象,该对象的MRO中没有任何类,该类包含有关所提供的参数的信息。class VaradicType(Generic[T, S]):
pass
cls = VaradicType[int, float]().__class__
print('__args__' in cls) # False
一种解决方案
解决此问题的一种可能方法是在实例化类
VaradicType
的对象时向其添加有关通用参数的信息。首先(按照前面的代码片段),我们将一个元类添加到
VaradicType
:class VaradicType(Generic[T, S], metaclass=GenericMixin):
pass
我们可以使用以下事实:如果
__getitem__
在元类上定义,则其优先级高于__class_getitem__
以便绕过Generic.__class_getitem__
class GenericMixin(type):
def __getitem__(cls, items):
return GenericAliasWrapper(cls.__class_getitem__(items))
现在,
VaradicType[int, float]
等效于GenericMixin.__getitem__(VaradicType, (int, float))
,它将返回类GenericAliasWrapper
的对象(用于“包装” typing._GenericAlias
实例):class GenericAliasWrapper:
def __init__(self, x):
self.wrapped = x
def __call__(self, *args, **kwargs):
obj = self.wrapped.__call__(*args, **kwargs)
obj.__dict__['__args__'] = self.wrapped.__args__
return obj
现在,如果您具有
cls=VaradicType[int, float]
,则代码cls()
将等效于GenericAliasWrapper( VaradicType.__class_getitem__((int, float)) ).__call__()
,这将创建类VaradicType
的新实例,并将属性__args__
添加到其字典中。例如:
VaradicType[int, float]().__args__ # (<class int>, <class float>)
关于python - 使用可变参数泛型进行方法返回专用化,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57128053/