首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何为过滤器方法编写类型提示/使用typevar?

如何为过滤器方法编写类型提示/使用typevar?
EN

Stack Overflow用户
提问于 2020-01-29 00:14:35
回答 1查看 2K关注 0票数 4

我在Python中有一个helper方法,它返回每个方法的方法列表和带注释的数据。所以这是一堆名单。带注释的数据由Attribute类表示。

定义如下:

代码语言:javascript
运行
复制
# 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的列表。

代码语言:javascript
运行
复制
# 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获得一个返回值,如下所示:

代码语言:javascript
运行
复制
methods:Dict[Callable, List[UserAttribute]] = prog.GetMethods(filter=UserAttribute)

这种方法复制了大量代码。

可能的解决方案2:可以将Dict[Callable, List[UserAttribute]]抽象成某种新的泛型,这样我就可以使用:

代码语言:javascript
运行
复制
# pseudo code
UserGeneric[G] := Dict[Callable, List[G]]

# shorter usage
methods:UserGeneric[UserAttribute] = prog.GetMethods(filter=UserAttribute)

可能的解决方案3: --充其量--我想使用这样的TypeVar

代码语言:javascript
运行
复制
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)

最后,这是打字手册页中所示的简单示例的一个更复杂的变体:

代码语言:javascript
运行
复制
T = TypeVar("T")

def getElement(l: List[T]) -> T:
  pass

最后一个问题:

如何将TypeVar T约束到类及其所有子类的特定对象,而不需要像上面的str与字节示例那样的联合。

这个问题与https://github.com/Paebbels/pyAttributes有关。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-01-30 02:33:16

不幸的是,TypeVar期望至少有两个约束,如T = TypeVar("T", str, byte)

实际上,TypeVar只能使用一个约束。要做到这一点,您可以执行类似于T = TypeVar("T", bound=str)的操作。

关于更多细节,我建议阅读具有上界的TypeVars上的类型文档 -令人遗憾的是,官方的打字文档不够完善,而且常常非常简短地涵盖像TypeVars这样的重要概念。

因此,这意味着您可以通过这样做来解决您的问题:

代码语言:javascript
运行
复制
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()]))

一些注意事项:

  1. 我给我的TypeVar TAttr取名,而不是Attr。您想让读者知道签名中的“占位符”是什么,所以半通用的约定是在TypeVars前加上T_T。(如果您不需要一个上限,而是想要一个真正的开放占位符,则惯例是使用一个大写字母,比如TS。)
  2. GetMethods(...)中,您需要执行AttributeFilter[TAttr]。如果只执行AttributeFilter,这实际上等同于执行AttributeFilter[Any]。这是出于同样的原因,为什么执行List意味着与List[Any]相同的事情。
  3. 我正在重用TAttr来定义类型别名和GetMethods,主要是为了方便,但是您也可以创建一个新的TypeVar,如果您真的需要的话,可以将它用于GetMethods:这意味着完全相同的事情。
  4. reveal_type(...)是一个特殊的伪函数,某些类型检查器(例如,mypy和pyre)会理解:它让类型检查器打印出它认为表达式的类型。
  5. 并非所有类型检查器(如mypy)都支持为泛型类型设置默认参数。如果您的类型检查程序出现故障,您可能可以通过使用无arg和单arg变体创建重载来解决这个问题。
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59958957

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档