我试图用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,我收到警告“ {{1}”的警告“类'Node'的未解决的属性引用'get_string'”和“类'Node'的未解决的属性引用'get_integer'” }和n.get_string()
行。
我尝试为类型变量n.get_integer()
指定各种covariant
和contravariant
修饰符。在Python 3.7中,我还尝试通过添加T
并从from __future__ import annotations
的返回值提示中删除引号来使用PEP 563。这些都没有影响。
我尝试在Node.depth_first_search
中创建类似于以下内容的“类型转换”方法。
StringNode
这可以解决 def depth_first_search(self) -> Iterable[StringNode]:
return super().depth_first_search()
块中的警告,但是现在我收到关于此方法返回值的“预期类型'Iterable [StringNode]',得到了'Iterable [Node]'”警告
如何重写类型提示,以免收到警告?
答案 0 :(得分:2)
当a
是StringNode
时,a.depth_first_search()
返回Iterable[Node[str]]
,而不是Iterable[StringNode]
。
在这种情况下,您可能不应该拥有那些get_string
和get_integer
方法。只需让客户直接访问content
,或者如果您出于某种原因被确定要使用吸气剂,请在get_content(self) -> T
基类中使其成为Node
。
我认为Python的类型注释不支持让您的depth_first_search
返回想要的子类类型的可迭代对象,而无需显式强制转换或Any
。您将需要表达一个事实,即self
与self.children
的元素具有相同的类型,而我看不到这样做的方法。
答案 1 :(得分:0)
如果将Node
设为基类,似乎没有一种方法可以使用Python的类型提示来获得所需的结果。但是,我可以通过将Node
更改为mixin来使其工作。
from typing import TypeVar, Iterable, Generic
T = TypeVar("T")
class NodeMixin(Generic[T]):
def __init__(self, *children: T):
self.children = children
def depth_first_search(self) -> Iterable[T]:
yield self
for child in self.children:
yield from child.depth_first_search()
class StringNode(NodeMixin["StringNode"]):
def __init__(self, content: str, *children: T):
super().__init__(*children)
self.content = content
def upper(self) -> str:
return self.content.upper()
class IntegerNode(NodeMixin["IntegerNode"]):
def __init__(self, content: int, *children: T):
super().__init__(*children)
self.content = content
def add_five(self) -> int:
return self.content + 5
if __name__ == "__main__":
t = StringNode("apple", StringNode("banana"), StringNode("pear"))
for n in t.depth_first_search():
print(n.upper())
t = IntegerNode(1, IntegerNode(2), IntegerNode(3))
for n in t.depth_first_search():
print(n.add_five())
这可以正常运行,PyCharm不会给我任何警告。此外,如果我错误地尝试对upper
返回的错误类型的对象调用add_five
或NodeMixin.depth_first_search
,也会收到警告。