如何在python中使用正确的递归和副作用

时间:2013-01-07 15:51:00

标签: python recursion side-effects

在树形结构中,我试图找到分支的所有叶子。这是我写的:

def leafs_of_branch(node,heads=[]):
    if len(node.children()) == 0:
        heads.append(str(node))
    else:    
        for des in node.children():
           leafs_of_branch(des)
    return heads


leafs_of_branch(node)

我不知道为什么,但对我来说感觉不对。它有效,但我想知道是否有更好的方法来使用递归而不创建heads参数。

5 个答案:

答案 0 :(得分:2)

def leafs_of_branch(node,heads=[]):

总是一个坏主意。更好的是

def leafs_of_branch(node,heads=None):
    heads = heads or []

否则你总是对leafs_of_branch使用相同的列表。在你的具体情况下,它可能是o.k.,但迟早你会遇到问题。

我建议:

def leafs_of_branch(node):
    leafs = []
    for des in node.children():
        leafs.extend(leafs_of_branch(des))
    if len(leafs)==0:
        leafs.append(str(node))
    return leafs

leafs_of_branch(node)

我没有做if len(node.children()==0,而是在下降到所有(可能为零)的孩子后检查len(叶子)。因此我只调用node.children()一次。

答案 1 :(得分:1)

我相信这应该有效:

def leafs_of_branch(node):
    if len(node.children()) == 0:
       return [str(node)]
    else:
       x = []
       for des in node.children():
           x += leafs_of_branch(des)  #x.extend(leafs_of_branch(des)) would work too :-)
       return x

它不是很漂亮,可能会更加浓缩,但我试图尽可能地保留原始代码的形式,以使其显而易见。

如果您多次调用它,原始版本将无法正常工作,因为当您附加到heads列表时,该列表实际上会在调用之间保存。

答案 2 :(得分:1)

只要递归,你就是正确的IMO;你在递归呼叫中错过了头部参数。无论如何它正在工作的原因是其他人说的,默认参数是全局的并且在调用之间重用。

如果你想避免递归altogheter,在这种情况下你可以使用队列或堆栈和循环:

def leafs_of_branch(node):
    traverse = [node]
    leafs = []

    while traverse:
        node = traverse.pop()
        children = node.children()

        if children:
            traverse.extend(children)
        else:
            leafs.append(str(node))

    return leafs

答案 3 :(得分:0)

首先,不要使用可变对象(列表,dicts等)作为默认值,因为默认值是全局的并且在函数调用之间重用:

def bad_func(val, dest=[]):
    dest.append(val)
    print dest

>>> bad_func(1)
[1]
>>> bad_func(2)
[1, 2]  # surprise!

因此,随之而来的电话会让事情完全出乎意料。

至于递归问题,我会像这样重写:

from itertools import chain

def leafs_of_branch(node):
    children = node.children()
    if not children:  # better than len(children) == 0
        return (node, )

    all_leafs = (leafs_of_branch(child) for child in children)
    return chain(*all_leafs)

答案 4 :(得分:0)

您也可以通过这种方式递归地定义迭代器。

def leafs_of_branch(node):
    if len(node.children()) == 0:
        yield str(node)
    else:    
        for des in node.children():
            for leaf in leafs_of_branch(des):
                yield leaf

leafs = list(leafs_of_branch(node))