使用可变参数泛型进行方法 return 专业化
Use variadic generics for method return specialization
目标是让以下伪代码在 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"]
和 returning 结果。它正在应用mixins。如果 return 类型不是所有应用的 mixins 的联合,那么静态分析会抱怨缺少字段和方法。有一个非提示代码,其中类型作为方法参数给出,但 return 类型是 Any
是最后的选择。
我已经遇到过这个问题,所以也许我可以解释一下。
问题
假设我们有下一个 class 定义:
T = TypeVar('T')
S = TypeVar('S')
class VaradicType(Generic[T, S]):
pass
问题是 VaradicType[T, S]
调用 VaradicType.__class_getitem__((T, S))
,其中 return 是 class _GenericAlias
的对象。
然后,如果你这样做 cls = VaradicType[int, float]
,你可以反省用作索引的参数
cls.__args__
.
但是,如果像obj = cls()
这样实例化一个对象,则不能obj.__class__.__args__
。
这是因为 _GenericAlias
实现了 __call__
方法 return 直接是 VaradicType
的一个对象,它的 MRO 中没有包含参数信息的任何 class已提供。
class VaradicType(Generic[T, S]):
pass
cls = VaradicType[int, float]().__class__
print('__args__' in cls) # False
一个解决方案
解决此问题的一种可能方法是在实例化 class VaradicType
的对象时添加有关通用参数的信息。
首先(在前面的代码片段之后),我们将添加一个元class到VaradicType
:
class VaradicType(Generic[T, S], metaclass=GenericMixin):
pass
我们可以利用这样一个事实,即如果 __getitem__
它在 metaclass 上定义,则优先于 __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))
并且它将 return class GenericAliasWrapper
的对象(它用于 "wrap" 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__()
创建 class VaradicType
并且还将属性 __args__
添加到其字典中。
例如:
VaradicType[int, float]().__args__ # (<class int>, <class float>)
目标是让以下伪代码在 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"]
和 returning 结果。它正在应用mixins。如果 return 类型不是所有应用的 mixins 的联合,那么静态分析会抱怨缺少字段和方法。有一个非提示代码,其中类型作为方法参数给出,但 return 类型是 Any
是最后的选择。
我已经遇到过这个问题,所以也许我可以解释一下。
问题
假设我们有下一个 class 定义:
T = TypeVar('T')
S = TypeVar('S')
class VaradicType(Generic[T, S]):
pass
问题是 VaradicType[T, S]
调用 VaradicType.__class_getitem__((T, S))
,其中 return 是 class _GenericAlias
的对象。
然后,如果你这样做 cls = VaradicType[int, float]
,你可以反省用作索引的参数
cls.__args__
.
但是,如果像obj = cls()
这样实例化一个对象,则不能obj.__class__.__args__
。
这是因为 _GenericAlias
实现了 __call__
方法 return 直接是 VaradicType
的一个对象,它的 MRO 中没有包含参数信息的任何 class已提供。
class VaradicType(Generic[T, S]):
pass
cls = VaradicType[int, float]().__class__
print('__args__' in cls) # False
一个解决方案
解决此问题的一种可能方法是在实例化 class VaradicType
的对象时添加有关通用参数的信息。
首先(在前面的代码片段之后),我们将添加一个元class到VaradicType
:
class VaradicType(Generic[T, S], metaclass=GenericMixin):
pass
我们可以利用这样一个事实,即如果 __getitem__
它在 metaclass 上定义,则优先于 __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))
并且它将 return class GenericAliasWrapper
的对象(它用于 "wrap" 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__()
创建 class VaradicType
并且还将属性 __args__
添加到其字典中。
例如:
VaradicType[int, float]().__args__ # (<class int>, <class float>)