如何展平嵌套字典而不递归?

时间:2018-09-29 02:44:53

标签: python python-3.x

我碰到了可以使字典扁平化的功能:

def flatten(dictionnary, container=None):
    if container is None:
        container = []
    for k, v in dictionnary.items():
        container.append(k)
        if v:
            flatten(v, container)
    return container

为了测试它,我创建了一个嵌套n的字典,如下所示:

nesteddict = {}
for i in range(n, 0, -1):
    emptydict = {}
    emptydict[i] = nesteddict
    nesteddict = emptydict

该功能在n小于999时起作用,否则它将达到递归限制:

RecursionError: maximum recursion depth exceeded while calling a Python object

因此,经过一点搜索之后,似乎any recursive function can rewritten to iteration了,但是我看不到对于必须产生相同结果的函数该怎么做。

在玩此游戏时遇到的另一个怪异问题是,如果我尝试下面的n >= 998代码:

nesteddict = {}
for i in range(n, 0, -1):
    emptydict = {}
    emptydict[i] = nesteddict
    nesteddict = emptydict
print(nesteddict)

我遇到递归错误:

RecursionError: maximum recursion depth exceeded while getting the repr of an object

这很奇怪,因为我在这里看不到任何递归。

3 个答案:

答案 0 :(得分:6)

您应该将字典的迭代器保存在堆栈中,而不是将字典保存在堆栈中。

这样,您可以在命令中恢复迭代器。

此外,因为您是按顺序暂停和恢复迭代器的执行,所以结果始终将根据字典的顺序。

顺便说一句,@ iBug,字典按照3.7的Python规范进行排序

def flatten(dictionary, container=None):
    if container is None:
        container = []
    iterators = []
    iterator = iter(dictionary.items())
    while True:
        for k, v in iterator:
            container.append(k)
            if v:
                # Save the current iterator for later
                iterators.append(iterator)
                # Run on the new dict
                iterator = iter(v.items())
                break

        # Current iterator is done, fetch the next one
        else:
            try:
                iterator = iterators.pop()
            except IndexError:
                return container

print(flatten({1: None, 2: {3: None, 4: None}, 5: None}))
[1, 2, 3, 4, 5]

答案 1 :(得分:3)

从逻辑上讲,嵌套字典(和列表)是一种递归,因此,如果要避免逻辑递归,那是不可能的。

但是,由于递归只是递归,因此您可以保留自己的堆栈并在循环中进行模拟:

def flatten(dct, c=None):
    if c is None:
        c = []
    stack = [dct]
    while stack:  # non-empty
        d = stack.pop()
        for k, v in d.items():
            c.append(k)
            if v:
                stack.append(v)
    return c

此函数使用自定义堆栈很好地模拟了函数递归的行为。

有一个潜在缺点:理论上,像这样的字典

{1: None, 2: {3: None, 4: None}, 5: None}

应该展平为[1, 2, 3, 4, 5],而此方法将给出[1, 2, 5, 3, 4]。这就像在图形上进行DFS搜索还是BFS搜索。

但是,由于字典是无序的,所以这不是大问题(除非您使用的是collections.OrderedDict),这就是为什么我说这是一个潜力缺点。

答案 2 :(得分:-1)

如果您想不进行递归操作,那是不可能的。

因此,这里是RecursionError的解决方案。

基于doc of python. 您可以使用sys.getrecursionlimit()来查看递归限制。您也可以使用sys.setrecursionlimit()来设置上限。