Python动态生成树的迭代逻辑问题咨询
索引树生成异常问题分析与修复
问题背景
需要实现索引树生成功能:给定根节点的两组索引(_indices_lhs和_indices_rhs),对同时出现在左右两侧的每个索引生成子节点。例如:
- 索引组
ik与kn生成子节点,其子节点_indices_lhs为[i],_indices_rhs为[n] - 索引组
ikn与ikn(左右完全相同)预期生成3个子节点(kn&kn、in&in、ik&ik),每个子节点再各自生成2个子节点
采用while循环迭代树:创建根节点后维护已生成子节点的引用队列,队列非空则继续生成后续子节点。
异常现象
- 循环无限运行,不断生成更多元素
- 部分节点的子节点数量异常增长(如深度为2的节点出现数千个子节点)
- 深度为2的节点未被迭代生成子节点,但其
_children成员却不断添加子节点
问题根源分析
1. 类属性与实例属性混淆(核心问题)
你有C++开发背景,容易默认类中定义的变量是实例独有,但Python中类定义里直接赋值的变量是类属性,所有实例共享。原代码中Node类的_parent、_depth、_children等变量都是类属性,导致:
- 所有
Node实例共用同一个_children列表,一个实例添加子节点时,所有实例的_children都会同步增长 - 不同节点的子节点互相干扰,出现子节点数量异常、未迭代节点的
_children被修改的情况
2. 不必要的对象复制
在generate_further_paths中,创建child后执行self._children.append(copy.deepcopy(child)),这会额外复制一个完全相同的Node实例,不仅浪费资源,还可能导致队列中出现重复节点,加剧循环无限运行的问题。
修复方案
步骤1:将类属性改为实例属性
在__init__方法中初始化所有实例独有的属性,确保每个Node实例拥有独立的_children、_children_contractions等变量:
import copy class Node: def __init__(self, indices_lhs: list[str], indices_rhs: list[str], depth: int = 0): # 初始化实例属性,每个实例独立拥有 self._parent = None self._depth = depth self._indices_lhs = indices_lhs self._indices_rhs = indices_rhs self._children_contractions = [] self._children = [] 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) # 直接添加child引用,无需复制 self._children.append(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>'
步骤2:简化队列操作(可选,优化逻辑)
循环代码中copy.copy是浅复制,对列表来说足够,但可以简化写法,同时避免不必要的复制:
free_lhs = ["i", "k", "n"] free_rhs = ["i", "k", "n"] beginNode = Node(free_lhs, free_rhs, 0) beginNode.generate_further_paths() # 直接用列表引用,无需copy.copy queue = beginNode._children.copy() while queue: nq = [] for child in queue: child.generate_further_paths() # 扩展新队列,无需额外复制 nq.extend(child._children) queue = nq
修复后效果
- 每个节点的
_children列表独立,子节点数量符合预期(根节点3个子节点,每个子节点2个子节点,深度2的节点不再生成子节点) - 循环会在队列清空后正常终止,不会无限运行
- 深度2的节点不会被错误添加子节点
内容的提问来源于stack exchange,提问作者Cherry Toska
相关产品推荐
相关产品推荐

