Python 中树迭代中的一些问题,而树可以动态生成

Some Issues in Tree Iteration in Python while the Tree Can be Dynamically Generated on the Fly

提问人:Cherry Toska 提问时间:6/29/2023 更新时间:7/3/2023 访问量:19

问:

我正在编写以下功能。一个根节点有 2 组索引(_indices_lhs_indices_rhs),在左侧和右侧的每个索引之前,它都会创建一个子节点,其中

例如,ik、kn 将有一个子项,其中子项的 _indices_lhs_indices_rhs 分别为 i 和 n。例如,在 ikn、ikn(左右两侧的相同索引)的情况下,预期结果是您将有 3 个孩子 kn, kn;在,在;IK、IK 和 OFC 这 3 个孩子将各多生 2 个孩子。为了实现这一点,我用一个while循环来迭代树。我将创建根节点,然后保留已创建子节点的引用队列,然后只要此队列不为空,我就会进行迭代,以创建可能创建节点的更多子节点/节点。当我遍历所有队列并且它是空的时,我就可以知道不能再生成子项了。我为此目的实现的代码如下:

该节点如下所示,子节点是在成员函数generate_further_paths中创建的。我创建所有可能的子组合,并将它们添加到成员变量_children。

class Node:
    _parent = None
    _depth = 0
    _indices_lhs = list()
    _indices_rhs = list()
    _children_contractions = list()
    _children              = list()

    def __init__(self, indices_lhs: list[str], indices_rhs: list[str], depth: int = 0):
        self._indices_lhs = indices_lhs
        self._indices_rhs = indices_rhs
        self._depth = depth
        print(self)

    def generate_further_paths(self):
        if len(self._indices_lhs) == 0 or len(self._indices_rhs) == 0:
            return

        if len(self._indices_lhs) == 1 and len(self._indices_rhs) == 1 \
            and self._indices_lhs[0] == self._indices_rhs[0]:
            return

        print("D1: ", self._depth, ": ", self._indices_lhs, " | ", self._indices_rhs)
        intersected_indices = []
        for i in self._indices_lhs:
            if i in self._indices_rhs:
                intersected_indices.append(i)

        if len(intersected_indices) == 0:
            return

        print("Intersected indices: ", intersected_indices)
        for ii in intersected_indices:
            child_lhs = copy.deepcopy(self._indices_lhs)
            child_lhs.remove(ii)
            child_rhs = copy.deepcopy(self._indices_rhs)
            child_rhs.remove(ii)
            child = Node(child_lhs, child_rhs, self._depth + 1)
            self._children_contractions.append(ii)
            self._children.append(copy.deepcopy(child))

    def __str__(self):
        return f"Node constructed depth {self._depth}:" + "".join(self._indices_lhs) + " * " + \
            "".join(self._indices_rhs) + ", " + str(len(self._children)) + " children"

    def __repr__(self):
        return '<tree node representation>'

生成树的循环方法如下。我正在尝试确保队列元素是对原始元素的引用:

free_lhs = ["i", "k", "n"]
free_rhs = ["i", "k", "n"]
beginNode = Node(free_lhs, free_rhs, 0)
beginNode.generate_further_paths()
queue = copy.copy(beginNode._children)
while len(queue) != 0:
    nq = list()
    for child in queue:
        child.generate_further_paths()
        nnq = copy.copy(child._children)
        nq += nnq
    queue = nq

我认为有些事情我理解发生了一些错误,因为在这个循环中我不断获得更多元素,并且树中的一些节点增加了它们的子节点的数量。输出示例:

D1:  1 :  ['i', 'k']  |  ['i', 'k']
Intersected indices:  ['i', 'k']
Node constructed depth 2:k * k, 4333 children
Node constructed depth 2:i * i, 4334 children

但是在深度 2 处构造的节点永远不会在生成时迭代,但它们的子节点会不断推回其成员变量_children。我更多地来自 C++ 领域,并且有参考,我认为这不应该发生,所以我认为这里的东西没有通过引用来捕获。

由于名称与真实对象绑定,因此我希望这种方法不会成为问题。我迭代每个子项,用生成的子项列表覆盖上一个队列。我需要一些关于我的代码行为的进一步见解,因为我已经阅读了这篇文章。 以及其他一些来源,但我无法掌握它。

python 按引用传递

评论

1赞 slothrop 6/29/2023
self._children将是您定义的变量,而不是实例变量。是否要在方法中初始化为实例变量?事实上,我怀疑你在这里不需要任何类变量,所有的类变量实际上都应该是实例变量。self._children = list()__init__
1赞 slothrop 6/29/2023
请参阅:stackoverflow.com/questions/1680528/... 和 stackoverflow.com/questions/8959097/...
1赞 Cherry Toska 6/29/2023
@slothrop 感谢您的评论,我注意到该列表不知何故没有丢失值。关于python,我不知道的是,当像这样初始化时,他们是类成员。(通过每个实例共享)这是我根据我的 C++ 习惯做的事情。谢谢你的评论,它解决了我的问题。
1赞 slothrop 6/29/2023
欢迎!同样值得一提的是,Python 具有“数据类”,其中方法体之外的声明确实会创建实例属性而不是类属性:docs.python.org/3/library/dataclasses.html。当这种声明风格也存在时,一开始很容易混淆。

答:

0赞 Cherry Toska 7/3/2023 #1

我想发布@slothrop的评论作为答案,因为它解决了问题。

self._children = list()创建一个类变量,该变量在类的每个实例上共享。成员变量需要在类的方法中初始化。 __init__