Pae*_*els 4 python type-hinting python-typing
我在 Python 中有一个辅助方法,它返回方法列表以及每个方法的带注释的数据。所以它是一个列表字典。带注释的数据由类来表示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
Run Code Online (Sandbox Code Playgroud)
此语法和相应的类型检查工作正常。
因为我想改进它。
用户通常从类派生用户定义的属性Attribute。我想表达的是,如果用户传递像UserAttributeto 这样的派生类GetMethods,那就返回 s 列表的字典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)
Run Code Online (Sandbox Code Playgroud)
该代码可以毫无问题地执行,但 PyCharm 的类型检查器不知道最后一行(打印调用)中someData存在该字段。attribute
可能的解决方案 1:
我可以为每个变量使用 typehint 来获取返回值,GetMethods如下所示:
methods:Dict[Callable, List[UserAttribute]] = prog.GetMethods(filter=UserAttribute)
Run Code Online (Sandbox Code Playgroud)
这种方法复制了大量代码。
可能的解决方案2:
是否可以抽象Dict[Callable, List[UserAttribute]]为某种新的泛型,以便我可以使用:
# pseudo code
UserGeneric[G] := Dict[Callable, List[G]]
# shorter usage
methods:UserGeneric[UserAttribute] = prog.GetMethods(filter=UserAttribute)
Run Code Online (Sandbox Code Playgroud)
可能的解决方案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
Run Code Online (Sandbox Code Playgroud)
不幸的是,TypeVar预计至少有 2 个约束,例如T = TypeVar("T", str, byte).
最后,这是打字手册页中显示的简单示例的更复杂的变体:
T = TypeVar("T")
def getElement(l: List[T]) -> T:
pass
Run Code Online (Sandbox Code Playgroud)
最后一个问题:
如何将 TypeVar T 约束到类及其所有子类的某些对象,而不需要像上面的 str 与 byte 示例中那样的联合。
不幸的是,
TypeVar预计至少有 2 个约束,例如T = TypeVar("T", str, byte).
事实上,TypeVar只要有一个约束就可以工作。为此,您需要执行类似的操作T = TypeVar("T", bound=str)。
有关更多详细信息,我建议阅读有关具有上限的 TypeVars 的 mypy 文档- 遗憾的是,官方打字文档不是很完善,并且通常非常简短地涵盖了像具有上限的 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()]))
Run Code Online (Sandbox Code Playgroud)
一些注意事项:
我将我的 TypeVar 命名为TAttr,而不是Attr. 您想让读者清楚签名中的“占位符”是什么,因此半常见的约定是在您的 TypeVars 前面加上 或T前缀_T。(如果您不需要上限,而是想要一个真正开放式的占位符,则惯例是使用单个大写字母,例如Tor S。)
在GetMethods(...),你需要做的AttributeFilter[TAttr]。如果你只是这样做AttributeFilter,那实际上就相当于做AttributeFilter[Any]。List这就是为什么“doing”与“doing ”意思相同的原因List[Any]。
我重复使用TAttr定义类型别名,GetMethods主要是为了方便,但您也可以创建一个新的 TypeVar 并使用它(GetMethods如果您确实想要的话):这意味着完全相同的事情。
reveal_type(...)是一些类型检查器(例如 mypy 和pyre)可以理解的特殊伪函数:它使类型检查器打印出它认为表达式的类型是什么。
并非所有类型检查器(例如 mypy)都支持为泛型类型设置默认参数。如果您的类型检查器抱怨,您可能可以通过使用无参数和单参数变体创建重载来解决它。
| 归档时间: |
|
| 查看次数: |
3512 次 |
| 最近记录: |