Python使用“父”键从平面字典中创建嵌套结构

时间:2019-11-06 15:31:48

标签: python oop recursion

让我们考虑一下Python 3中的示例:

class SimpleObj:
    def __init__(self, own_id: int, parent_id: int):
        self.parent_id = parent_id
        self.own_id = own_id
        self.children_objects = []  # type: List[SimpleObj]

test_dict = {}
test_dict[0] = SimpleObj(own_id=0, parent_id=-1)  # -1 will mean root node
test_dict[1] = SimpleObj(own_id=1, parent_id=0)
test_dict[123] = SimpleObj(own_id=123, parent_id=1)
test_dict[5] = SimpleObj(own_id=5, parent_id=123)

创建递归嵌套结构的最Python方式是什么,例如,带有own_id=1的SimpleObj的children_objects列表中将填充一个元素,该元素将是带有own_id=123的SimpleObj ? 它闻起来与二叉树问题非常相似,但是我真的没有找到一个可行的解决方案来将这种字典变成树状对象的结构。

2 个答案:

答案 0 :(得分:1)

也许您可以创建一个小函数来处理新的SimpleObj的创建:

class SimpleObj:
    def __init__(self, own_id: int, parent_id: int):
        self.parent_id = parent_id
        self.own_id = own_id
        self.children_objects = []


def addSimpleObj(struct, own_id, parent_id):
    struct[own_id] = SimpleObj(own_id=own_id, parent_id=parent_id)
    if parent_id != -1:
        try:
            struct[parent_id].children_objects.append(struct[own_id])
        except IndexError:
            print('the parent_id does not exists')

test_dict = {}
addSimpleObj(test_dict, 0, -1)
addSimpleObj(test_dict, 1, 0)
addSimpleObj(test_dict, 123, 1)
addSimpleObj(test_dict, 5, 123)

这只是一个开始,但似乎可以解决问题。您可以对其进行修改,以处理例如尝试为已存在的密钥重新创建SimpleObj的情况。

答案 1 :(得分:0)

深入研究之后,我想出了自己的解决方案。有两个要解决的主要问题-递归和处理引用尚未出现的父节点的节点。这是我的解决方案。它尝试递归地将对象分配给其父母,如果失败,它将创建一个孤儿列表。然后,遍历孤立节点列表,直到所有节点都到位。我在多个配置上对此进行了测试,看起来它总是可以工作的(假设输入正确,这意味着没有重复或循环引用等)。

from __future__ import annotations
from typing import List


class SimpleObj:
    def __init__(self, parent_id: int, own_id: int):
        self.own_id = own_id
        self.parent_id = parent_id
        self.children_objects = []  # type: List[SimpleObj]

    def assign_to_children(self, what_to_assign: SimpleObj):
        if what_to_assign.parent_id == self.own_id:
            self.children_objects.append(what_to_assign)
            return True
        else:
            for child in self.children_objects:
                if child.assign_to_children(what_to_assign=what_to_assign):
                    return True

    def __repr__(self):
        return f"ID: {self.own_id}, PARENT: {self.parent_id}"

test_objects_list = [SimpleObj(own_id=69, parent_id=70),
                     SimpleObj(own_id=59, parent_id=71),
                     SimpleObj(own_id=70, parent_id=71),
                     SimpleObj(own_id=71, parent_id=1),
                     SimpleObj(own_id=1, parent_id=0),
                     SimpleObj(own_id=2, parent_id=1),
                     SimpleObj(own_id=44, parent_id=1),
                     SimpleObj(own_id=6, parent_id=44),
                     SimpleObj(own_id=14, parent_id=10),
                     SimpleObj(own_id=10, parent_id=1)]

def create_tree_from_list_of_obj(list_in: List[SimpleObj]):
    # find root
    root = [obj for obj in list_in if obj.parent_id == 0][0]
    list_in.remove(root)
    print(root)

    orphans = []
    # do the actual work
    for obj in list_in:
        found = root.assign_to_children(obj)
        print(f"Found {obj.own_id}") if found else print(f"Not found {obj.own_id}")
        if not found:
            orphans.append(obj)
            print(f"Adding orphaned node with id {obj.own_id}")

    while orphans:
        print(f"\nTrying to fill in {len(orphans)} orphaned nodes...")
        for orphaned_node in orphans[:]:
            found = root.assign_to_children(orphaned_node)
            if found:
                print(f"Found {orphaned_node.own_id}")
                orphans.remove(orphaned_node)
            else:
                print(f"Not found {orphaned_node.own_id}")
    return root

tree = create_tree_from_list_of_obj(test_objects_list)