泛型类型类的正确类型提示

2024-09-27 21:29:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试用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中,我分别得到了n.get_string()n.get_integer()行的警告“未解析属性引用'get_string'用于类'Node'”和“未解析属性引用'get_integer'用于类'Node'”

我尝试过为类型变量T指定各种covariantcontravariant修饰符。在Python3.7中,我还尝试使用PEP 563,添加from __future__ import annotations并从Node.depth_first_search的返回值提示中删除引号。所有这些都没有产生效果

我曾尝试在StringNode中创建如下“类型转换”方法

    def depth_first_search(self) -> Iterable[StringNode]:
        return super().depth_first_search()

这将处理__main__块中的警告,但现在我在该方法的返回值上得到了“预期类型'Iterable[StringNode]',而不是'Iterable[Node]'警告

如何重写类型提示以避免收到警告


Tags: selfnode类型searchgetstringdefinteger
2条回答

如果我将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没有给我任何警告。此外,如果我错误地尝试调用upperadd_fiveNodeMixin.depth_first_search返回的错误类型的对象,我会收到警告

aStringNode时,a.depth_first_search()返回Iterable[Node[str]],而不是Iterable[StringNode]

在这种情况下,您可能不应该使用那些get_stringget_integer方法。只要让客户机直接访问content,或者如果出于某种原因决定使用getter,则将其设置为get_content(self) -> T基类中的Node

我认为Python的类型注释不支持depth_first_search返回所需子类类型的iterable,而不使用显式强制转换或Any。您需要能够表达这样一个事实,即selfself.children的元素具有相同的类型,而我看不到实现这一点的方法

相关问题 更多 >

    热门问题