【发布时间】:2020-10-24 16:04:11
【问题描述】:
我正在尝试用 Python 编写一个树节点类。我有一个名为Node 的基类,它定义了树语义和实现包含不同类型内容的节点的子类。我想使用类型提示。
这是一个最小的实现,它创建字符串或整数的树,并以深度优先的方式枚举它们。
from typing import TypeVar, Generic, List, Iterable
T = TypeVar("T")
class Node(Generic[T]):
def __init__(self, content: T):
self.content = content
self.children: List[Node[T]] = []
def depth_first_search(self) -> Iterable["Node[T]"]:
yield self
for child in self.children:
yield from child.depth_first_search()
class StringNode(Node[str]):
def get_string(self) -> str:
return self.content
class IntegerNode(Node[int]):
def get_integer(self) -> int:
return self.content
if __name__ == "__main__":
a = StringNode("apple")
b = StringNode("banana")
c = StringNode("pear")
a.children = [b, c]
for n in a.depth_first_search():
print(n.get_string())
a = IntegerNode(1)
b = IntegerNode(2)
c = IntegerNode(3)
a.children = [b, c]
for n in a.depth_first_search():
print(n.get_integer())
此代码在运行时有效,但是,从 PyCharm 我收到警告“类 'Node' 的未解析属性引用 'get_string'”和 n.get_string() 的“类 'Node' 的未解析属性引用 'get_integer'”和n.get_integer() 行。
我尝试为类型变量T 指定各种covariant 和contravariant 修饰符。在 Python 3.7 中,我还尝试通过添加 from __future__ import annotations 并从 Node.depth_first_search 的返回值提示中删除引号来使用 PEP 563。这些都没有效果。
我尝试在StringNode 中创建类似以下的“类型转换”方法。
def depth_first_search(self) -> Iterable[StringNode]:
return super().depth_first_search()
这会处理__main__ 块中的警告,但现在我在此方法的返回值上收到“预期类型'Iterable[StringNode]',得到'Iterable[Node]'”警告。
如何重写类型提示以免收到警告?
【问题讨论】:
-
是的。在实例变量上也有类型提示没有效果。
-
因为您的
depth_first_search(self)总是返回 Node 对象的迭代器。只需直接访问内容就没有必要拥有一个泛型类来创建子类只是为了拥有子类特定的访问器......而且不需要那些子类 -
IOW
Node[str]和StringNode不一样 -
在这个简单的示例中,子类似乎过多,但在我真正的大型应用程序中,以这种方式组织事物很有帮助。
-
基本上,
n的类型是Node[str]。要么将n转换为StringNode,要么在StringNode内创建一个具有正确返回类型的新函数depth_first_search。
标签: python type-hinting