递归调用后,递归循环不会回到原位置

时间:2018-11-15 09:47:27

标签: python python-3.x recursion

我正在尝试使用Python 3.5中的递归循环来存储测试软件中的数据。

我不明白的是为什么Python不返回递归调用的第一个调用并继续循环。

我愿意实现什么?

我有一系列这样的词典:

testSuites = [{'id': '278', 'parent_id': '213', 'name': 'Initial release'}, {'id': '281', 'parent_id': '279', 'name': 'Screening'}, {'id': '279', 'parent_id': '278', 'name': 'Web'}, {'id': '282', 'parent_id': '213', 'name': 'Initial release'}]

我想拥有一个TestSuite对象,该对象将具有一组TestSuite子代,每个子代都有自己的子代子集(如一棵树)。

root (TestSuite, id=213)
    .array[ child1 (TestSuite, id=278), child2 (TestSuite, id=282) ]

child1 (TestSuite, id=278)
    .array[ child11 (TestSuite, id=279) ]

child11 (TestSuite, id=279)
    .array[ child111 (TestSuite, id=281) ]

在以下情况下会发生扭曲:

testSuites = [{'id': '278', 'parent_id': '213', 'name': 'Initial release'}, {'id': '281', 'parent_id': '279', 'name': 'Screening'}, {'id': '279', 'parent_id': '278', 'name': 'Web'}, {'id': '282', 'parent_id': '213', 'name': 'Initial release'},  {'name': 'Initial release', 'parent_id': '213', 'id': '283'}, {'name': 'Screening', 'parent_id': '283', 'id': '284'}]

我期望以下几点:

root (TestSuite, id=213)
    .array[ child1 (TestSuite, id=278), child2 (TestSuite, id=282), child3 (TestSuite, id=283) ]

child1 (TestSuite, id=278)
    .array[ child11 (TestSuite, id=279) ]

child11 (TestSuite, id=279)
    .array[ child111 (TestSuite, id=281) ]

child2 (TestSuite, id=282)
    .array[]

child3 (TestSuite, id=283)
    .array[ child31 (TestSuite, id=284) ]

我需要将它们作为数组(或者我可以变成数组的东西),因为我需要将结果提供给可以遍历数组以填充文档的库。

我当前的代码。

我有一个简单的TestSuite类,它表示上图中的每个节点:

class TestSuite:
    def __init__(self, testsuite_id = None):
            self.testsuite_id = testsuite_id
            self.hasChild = False
            self.child = None

然后我使用一个函数(我留下了调试消息以了解我正在接收的输出)在该词典数组中循环。

 1  def buildTestSuiteTree(parent, testSuites, loopDepth):
 2      print("[DEBUG] Entering in buildTestSuiteTree")
 3      print("[DEBUG] testSuites: {}".format(testSuites))
 4      print("[DEBUG] parent id: {}".format(parent.testsuite_id))
 5      print("[DEBUG] loopDepth: {}".format(loopDepth))
 6      tsChild = TestSuite()
 7      index = 0
 8      for testSuiteInfo in testSuites:
 9              print("[DEBUG] loopDepth: {}".format(loopDepth))
10              testSuiteInfo = decode(str(testSuiteInfo))
11              print("[DEBUG] testSuiteInfo: {}".format(testSuiteInfo))
12              print("[DEBUG] matching against parent id {}".format(parent.testsuite_id))
13              if  str(parent.testsuite_id) == testSuiteInfo['parent_id']:
14                      print("[DEBUG] FOUND mathing parent!")
15                      parent.hasChild = True
16                      parent.child = tsChild
17                      testSuites.pop(index)
18                      tsChild.testsuite_id = int(testSuiteInfo['id'])
19                      loopDepth += 1
20                      tsChild.child = buildTestSuiteTree(tsChild, testSuites, loopDepth)
21              index += 1
22      return tsChild

我已经知道这不会给我上图(child (id = 282))的最后一点,因为我需要为每个孩子使用一些数组,但是目前这不是我的问题:- ) loopDepth是我试图理解我的问题的尝试。

输出

>>> root = TestSuite()
>>> root.testsuite_id = 213
>>> t = buildTestSuiteTree(root, testSuites,0)
[DEBUG] Entering in buildTestSuiteTree
[DEBUG] testSuites: [{'name': 'Initial release', 'parent_id': '213', 'id': '278'}, {'name': 'Screening', 'parent_id': '279', 'id': '281'}, {'name': 'Web', 'parent_id': '278', 'id': '279'}, {'name': 'Initial release', 'parent_id': '213', 'id': '282'}]
[DEBUG] parent id: 213
[DEBUG] loopDepth: 0
[DEBUG] loopDepth: 0
[DEBUG] testSuiteInfo: {'id': '278', 'parent_id': '213', 'name': 'Initial release'}
[DEBUG] matching against parent id: 213
[DEBUG] FOUND mathing parent!
[DEBUG] Entering in buildTestSuiteTree
[DEBUG] testSuites: [{'name': 'Screening', 'parent_id': '279', 'id': '281'}, {'name': 'Web', 'parent_id': '278', 'id': '279'}, {'name': 'Initial release', 'parent_id': '213', 'id': '282'}]
[DEBUG] parent id: 278
[DEBUG] loopDepth: 1
[DEBUG] loopDepth: 1
[DEBUG] testSuiteInfo: {'id': '281', 'parent_id': '279', 'name': 'Screening'}
[DEBUG] matching against parent id: 278
[DEBUG] loopDepth: 1
[DEBUG] testSuiteInfo: {'id': '279', 'parent_id': '278', 'name': 'Web'}
[DEBUG] matching against parent id: 278
[DEBUG] FOUND mathing parent!
[DEBUG] Entering in buildTestSuiteTree
[DEBUG] testSuites: [{'name': 'Screening', 'parent_id': '279', 'id': '281'}, {'name': 'Initial release', 'parent_id': '213', 'id': '282'}]
[DEBUG] parent id: 279
[DEBUG] loopDepth: 2
[DEBUG] loopDepth: 2
[DEBUG] testSuiteInfo: {'id': '281', 'parent_id': '279', 'name': 'Screening'}
[DEBUG] matching against parent id: 279
[DEBUG] FOUND mathing parent!
[DEBUG] Entering in buildTestSuiteTree
[DEBUG] testSuites: [{'name': 'Initial release', 'parent_id': '213', 'id': '282'}]
[DEBUG] parent id: 281
[DEBUG] loopDepth: 3
[DEBUG] loopDepth: 3
[DEBUG] testSuiteInfo: {'id': '282', 'parent_id': '213', 'name': 'Initial release'}
[DEBUG] matching against parent id: 281

我的问题

我期望一旦Python到达第13行中没有匹配项的位置,因为它到达了递归循环的最低点,它将回到loopDepth中的“ up”位置回到“原始”第20行。

从那里,我希望Python继续在原始循环上循环,此时testSuites

testSuites: [{'name': 'Initial release', 'parent_id': '213', 'id': '282'}]

然后,用t.child的最后一个值覆盖testSuites

我从输出中看到的只是这些!

我看到Python会以某种方式停留在最低的循环深度,并尝试匹配它找不到的父ID(卡在最后一个孩子身上),然后……好吧,什么都没有。

我知道这可能不是有史以来最干净的代码或pythonesque,也许我可以使用一些树状库来完成这项工作(也许我会讲到这一点),但是我不明白为什么我的程序是表现如预期。

  1. 您能给我看看灯,并告诉我为什么我的代码有问题吗?
  2. 奖金问题:每个有孩子的孩子的财产hasChild都没有设置为yes。我猜这是由于另一个函数调用中的更新上下文所致,但是您可以确认是吗?

谢谢大家的关注!

更新

由于Eric的评论,我删除了数组的切片,它确实改善了代码的执行。这是经过修改的代码(我也修改了TestSuite以添加array属性):

def buildTestSuiteTree(parent, testSuites, loopDepth):
    tsChild = TestSuite()
    index = 0
    for testSuiteInfo in testSuites:
            testSuiteInfo = decode(str(testSuiteInfo))
            if  str(parent.testsuite_id) == testSuiteInfo['parent_id']:
                    parent.hasChild = True
                    tsChild.testsuite_id = int(testSuiteInfo['id'])
                    loopDepth += 1
                    tsChild.child = buildTestSuiteTree(tsChild, testSuites, loopDepth)
                    parent.array.append(tsChild)
            index += 1
    return tsChild

给定初始testSuites时,它可以很好地创建带有子级数组的第一级对象。 因此,它给出了(这是一个简化的表示):

root (TestSuite, id=213)
    .array[ child1 (TestSuite, id=278), child2 (TestSuite, id=282) ]

child1 (TestSuite, id=278)
    .array[ child11 (TestSuite, id=279) ]

child11 (TestSuite, id=279)
    .array[ child111 (TestSuite, id=281) ]

但是当我给出复杂数据时:

testSuites = [{'id': '278', 'parent_id': '213', 'name': 'Initial release'}, {'id': '281', 'parent_id': '279', 'name': 'Screening'}, {'id': '279', 'parent_id': '278', 'name': 'Web'}, {'id': '282', 'parent_id': '213', 'name': 'Initial release'}]

孩子的第一层是错的:

root (TestSuite, id=213)
    .array[ child1 (TestSuite, id=283), child2 (TestSuite, id=283), child3 (TestSuite, id=283) ]

child1 (TestSuite, id=283)
    .array[ child11 (TestSuite, id=279) ]

child11 (TestSuite, id=279)
    .array[ child111 (TestSuite, id=281) ]

child12 (TestSuite, id=284)
    .array[]

child2 (TestSuite, id=283)
    .array[ child21 (TestSuite, id=279) ]

child21 (TestSuite, id=279)
    .array[ child211 (TestSuite, id=281) ]

child22 (TestSuite, id=284)
    .array[]

child3 (TestSuite, id=283)
    .array[ child31 (TestSuite, id=279) ]

child31 (TestSuite, id=279)
    .array[ child311 (TestSuite, id=281) ]

child32 (TestSuite, id=284)
    .array[]

(这不是拼写错误,是相同对象的三倍...)

我还更新了“我愿意实现的目标”,因为我认为我的最终意图并不明确,而Poolka的答案也无法解决(或者至少我无法使其符合我的需求: -))。

1 个答案:

答案 0 :(得分:0)

如果我对您的理解正确,'d like to have an object that would go from the root to the last child like this ...。但是数据存储在列表中,所以您不知道每个父母的所有孩子。因此,您可以在每个递归步骤上遍历testSuites列表。在循环内,您将处理testSuites列表的第一个元素,将其从列表中弹出,并使用经过修改的列表testSuites[1:]进行递归调用。因此,您通过递归实现了循环。不要那样做这很奇怪而且效果很差。

这是我尝试解决问题的方法。 func1()用于创建所有TestSuite对象的字典。 func2()是一种递归函数,用于以您在问题中提供的方式打印TestSuite对象。

class TestSuite:

    def __init__(self, testsuite_id):
            self.testsuite_id = testsuite_id
            self.hasChild = False
            self.childs = set()

    def __repr__(self):
        str_1 = (
            'TestSutie({})'
            .format(self.testsuite_id))
        str_2 = (
            ' with childs {}'
            .format([child.testsuite_id for child in self.childs])
            if self.hasChild
            else ' with NO childs')
        return str_1 + str_2


def func1(testSuites):

    answer = dict()

    for suite in testSuites:
        if suite['id'] not in answer:
            answer[suite['id']] = TestSuite(suite['id'])
        if suite['parent_id'] not in answer:
            answer[suite['parent_id']] = TestSuite(suite['parent_id'])
        answer[suite['parent_id']].hasChild = True
        answer[suite['parent_id']].childs.add(answer[suite['id']])

    return answer


def func2(root, num=0):

    print('|   ' * num, end='')
    print('id = ', root.testsuite_id)

    for child in root.childs:
        print('|   ' * (num + 1))
        func2(child, num + 1)


testSuites = [
        {'id': '278', 'parent_id': '213', 'name': 'Initial release'},
        {'id': '281', 'parent_id': '279', 'name': 'Screening'},
        {'id': '279', 'parent_id': '278', 'name': 'Web'},
        {'id': '282', 'parent_id': '213', 'name': 'Initial release'},
        {'id': '283', 'parent_id': '213', 'name': 'Initial release'},
        {'id': '284', 'parent_id': '283', 'name': 'Screening'}]

answer = func1(testSuites)
print(*answer.values(), sep='\n')
print()
func2(root=answer['213'])

输出:

TestSutie(213) with childs ['283', '278', '282']
TestSutie(278) with childs ['279']
TestSutie(281) with NO childs
TestSutie(279) with childs ['281']
TestSutie(282) with NO childs
TestSutie(283) with childs ['284']
TestSutie(284) with NO childs

id =  213
|   
|   id =  278
|   |   
|   |   id =  279
|   |   |   
|   |   |   id =  281
|   
|   id =  282
|   
|   id =  283
|   |   
|   |   id =  284