Python通过对象和子对象递归,打印子深度数

时间:2012-04-02 22:06:29

标签: python tree parent-child

我有一个带有属性的简单类,该属性可以包含同一类的对象列表

class BoxItem:
  def __init__(self, name, **kw):
      self.name = name
      self.boxItems = []
      ... #more attributes here

box1 = BoxItem('Normal Box')
box2 = BoxItem('Friendly Box')
box3 = BoxItem('Cool Box')
box4 = BoxItem('Big Box', [box1, box2]) #contains some children
example = BoxItem('Example Box', [box4,box3]) #contains another level of children

使用我们的'示例'框对象,我想操纵它可能拥有的所有可能盒子的深度,并打印出如下格式的对象:

1 Example Box
 1.1 Big Box
  1.1.1 Normal Box
  1.1.2 Friendly Box
 1.2 Cool Box

不需要Tabbing,只是想清楚地显示树格式。我能够自己走下来并打印出他们的标题,但是我无法打印显示父/子关系的前面数字。 (1,1.1,1.2 ......)

提前感谢您的帮助:)

修改 以下是我迄今为止一直在使用的内容

def print_boxes(box_list):
  node_count = 0
  for box in box_list:
    node_count += 1
    print str(node_count)+' '+box.name #prints out the root box
    recursive_search(box,node_count)

 def recursive_search(box,node_count): #recursive automatically 
  level = 0
  for child in box.boxItems:
    level += 1
    for x in range(len(child.boxItems)):
      print x+1 #this prints out some proper numbers
    print "level: "+str(level) #experiment with level
    print child.name #prints out child box name
    recursive_search(child,node_count) #runs the function again  inside the function

2 个答案:

答案 0 :(得分:9)

我认为如果我发布一个如何执行此操作的工作示例,而不是通过代码遇到问题的位置,那么对您来说可能会更有帮助。我们可能会以这种方式更快地达到理解的程度。您的代码有正确的想法,它需要跟踪深度。但它唯一缺少的是嵌套深度(树)。它只知道先前的node_count,然后知道它当前的子计数。

我的示例使用闭包来启动深度跟踪对象,然后创建一个内部函数来执行递归部分。

def recurse(box):

    boxes = not isinstance(box, (list, tuple)) and [box] or box

    depth = [1]

    def wrapped(box):

        depthStr = '.'.join([str(i) for i in depth])
        print "%s %s" % (depthStr, box.name)

        depth.append(1)
        for child in box.boxItems:
            wrapped(child)
            depth[-1] += 1
        depth.pop()

    for box in boxes:
        wrapped(box)
        depth[0] += 1

示例的示例输出:

>>> recurse(example)
1 Example Box
1.1 Big Box
1.1.1 Normal Box
1.1.2 Friendly Box
1.2 Cool Box

>>> recurse([example, example])
1 Example Box
1.1 Big Box
1.1.1 Normal Box
1.1.2 Friendly Box
1.2 Cool Box
2 Example Box
2.1 Big Box
2.1.1 Normal Box
2.1.2 Friendly Box
2.2 Cool Box

打破这种局面:

我们首先接受一个box参数,如果你只传入一个盒子项,它会自动将它本地转换为一个列表。这样你就可以传递一个盒子对象,或者它们的列表/元组。

depth是我们的深度跟踪器。它是一个我们将在递归发生时建立和缩小的整数列表。它从第一个项目/第一级别的1开始。随着时间的推移,它看起来像这样:[1,1,2,3,1]取决于它穿越的深度。 这是我的代码与您的代码之间的主要区别。每次递归都可以访问此状态。

现在我们有了这个内部wrapped函数。它将获取当前的盒子项并打印它,然后迭代它的子项。我们通过加入当前深度列表,然后是名称来获取我们的打印字符串。

每次我们下载到子列表中时,我们都会在深度列表中添加一个起始级别1,当我们从该子循环中退出时,我们会再次将其弹回。对于该循环中的每个孩子,我们将最后一个项目递增。

wrapped内部函数之外,我们通过循环遍历初始框,调用wrapped然后递增第一级来启动整个事务。

内部包装函数在闭包中使用深度列表。我愿意打赌其他人可以对此提供进一步的改进,但这是我想出的一个例子。

请注意函数的参数

我们也可以设计recurse来取代可变长度参数列表,而不是检查列表。它看起来像这样(并且会摆脱第一次boxes =检查):

def recurse(*boxes):
    #boxes will always come in as a tuple no matter what

>>> recurse(example)
>>> recurse(example, example, example)

如果您最初从一个盒子项目列表开始,您可以通过执行以下操作来传递它:

>>> boxes = [example, example, example]
>>> recurse(*example)    # this will unpack your list into args

答案 1 :(得分:1)

您有两种选择:

  1. 跟踪附加信息,作为递归中的附加参数,例如: myRecursiveFunction(..., ancestry=[])
  2. 让每个BoxItem跟踪其父级,只要它嵌入在BoxItem中(在__init__构造函数中,为每个子级设置child.parent = self)。如果你打算在一个以上的盒子里安装一个BoxItem,那就不好了。