我在Python中有一个helper方法,它返回每个方法的方法列表和带注释的数据。所以这是一堆名单。带注释的数据由Attribute类表示。
定义如下:
# A filter predicate can be either an attribute object or a tuple/list of attribute objects.
AttributeFilter = Union['Attribute', Iterable['Attribute'], None]
# A class offering a helper method
class Mixin:
def GetMethods(self, filter: AttributeFilter=Attribute) -> Dict[Callable, List[Attribute]]:
pass这个语法和相应的类型检查工作得很好。
当然,我想改进一下。
用户通常从类Attribute派生用户定义的属性.我想表示的是,如果用户将像UserAttribute这样的派生类传递给GetMethods,即返回一组UserAttribute的列表。
# Some user-defined attribute and some public data in it
class UserAttribute(Attribute):
someData: str
# Create a big class
class Big(mixin):
# Annotate a method with meta information
@UserAttribute("hello")
def method(self):
pass
# Create an instance
prog = Big()
# search for all methods that have 'UserAttribute' annotations
methods = prog.GetMethods(filter=UserAttribute)
for method, attributes in methods:
for attribute in attributes:
print(attribute.someData)这段代码可以在没有问题的情况下执行,但是PyCharm的类型检查器不知道在最后一行(打印调用)中存在someData字段attribute。
可能的解决方案1: I可以对每个变量使用类型提示,从GetMethods获得一个返回值,如下所示:
methods:Dict[Callable, List[UserAttribute]] = prog.GetMethods(filter=UserAttribute)这种方法复制了大量代码。
可能的解决方案2:可以将Dict[Callable, List[UserAttribute]]抽象成某种新的泛型,这样我就可以使用:
# pseudo code
UserGeneric[G] := Dict[Callable, List[G]]
# shorter usage
methods:UserGeneric[UserAttribute] = prog.GetMethods(filter=UserAttribute)可能的解决方案3: --充其量--我想使用这样的TypeVar:
Attr = TypeVar("Attr", Attribute)
# A filter predicate can be either an attribute object or a tuple/list of attribute objects.
AttributeFilter = Union[Attr, Iterable[Attr], None]
# A class offering a helper method
class Mixin:
def GetMethods(self, filter: AttributeFilter=Attribute) -> Dict[Callable, List[Attr]]:
pass不幸的是,TypeVar期望至少有两个约束,如T = TypeVar("T", str, byte)。
最后,这是打字手册页中所示的简单示例的一个更复杂的变体:
T = TypeVar("T")
def getElement(l: List[T]) -> T:
pass最后一个问题:
如何将TypeVar T约束到类及其所有子类的特定对象,而不需要像上面的str与字节示例那样的联合。
发布于 2020-01-30 02:33:16
不幸的是,
TypeVar期望至少有两个约束,如T = TypeVar("T", str, byte)。
实际上,TypeVar只能使用一个约束。要做到这一点,您可以执行类似于T = TypeVar("T", bound=str)的操作。
关于更多细节,我建议阅读具有上界的TypeVars上的类型文档 -令人遗憾的是,官方的打字文档不够完善,而且常常非常简短地涵盖像TypeVars这样的重要概念。
因此,这意味着您可以通过这样做来解决您的问题:
from typing import TypeVar, Union, Iterable, Dict, Callable, List
class Attribute: pass
class UserAttribute(Attribute): pass
TAttr = TypeVar("TAttr", bound=Attribute)
AttributeFilter = Union[TAttr, Iterable[TAttr], None]
class Mixin:
def GetMethods(self,
filter: AttributeFilter[TAttr] = Attribute,
) -> Dict[Callable, List[TAttr]]:
pass
m = Mixin()
# Revealed type is 'builtins.dict[def (*Any, **Any) -> Any, builtins.list[test.UserAttribute*]]'
reveal_type(m.GetMethods([UserAttribute(), UserAttribute()]))一些注意事项:
TAttr取名,而不是Attr。您想让读者知道签名中的“占位符”是什么,所以半通用的约定是在TypeVars前加上T或_T。(如果您不需要一个上限,而是想要一个真正的开放占位符,则惯例是使用一个大写字母,比如T或S。)GetMethods(...)中,您需要执行AttributeFilter[TAttr]。如果只执行AttributeFilter,这实际上等同于执行AttributeFilter[Any]。这是出于同样的原因,为什么执行List意味着与List[Any]相同的事情。TAttr来定义类型别名和GetMethods,主要是为了方便,但是您也可以创建一个新的TypeVar,如果您真的需要的话,可以将它用于GetMethods:这意味着完全相同的事情。reveal_type(...)是一个特殊的伪函数,某些类型检查器(例如,mypy和pyre)会理解:它让类型检查器打印出它认为表达式的类型。https://stackoverflow.com/questions/59958957
复制相似问题