使用递归函数时处理python全局变量

时间:2011-07-12 08:09:16

标签: python global-variables

我制作了一个从HTML文件中提取文本的程序。它递归HTML文档并返回标记列表。例如,

输入< li>没有办法< b>你< / b>这样做< / li>

输出 ['不','方式','你','是'......]

这是一个高度简化的伪代码:

def get_leaves(node):
    kids=getchildren(node)
    for i in kids:
        if leafnode(i):
            get_leaves(i)
        else:
            a=process_leaf(i)
            list_of_leaves.append(a)

def calling_fn():
    list_of_leaves=[] #which is now in global scope
    get_leaves(rootnode)
    print list_of_leaves    

我现在正在使用来自调用函数的全局范围 list_of_leaves 。 calling_fn()声明了这个变量,get_leaves()追加到这个变量。

我的问题是,如何修改我的功能,以便能够执行类似list_of_leaves = get_leaves(rootnode)的操作,即不使用全局变量?

我不希望函数的每个实例都复制列表,因为列表可能会变得非常大。

请不要批评这个特定伪代码的设计,因为我简化了这一点。它用于另一个目的:使用BeautifulSoup

提取令牌以及相关标签

5 个答案:

答案 0 :(得分:11)

您可以将结果列表作为可选参数传递。

def get_leaves(node, list_of_leaves=None):
    list_of_leaves = [] if list_of_leaves is None else list_of_leaves
    kids=getchildren(node)
    for i in kids:
        if leafnode(i):
            get_leaves(i, list_of_leaves)
        else:
            a=process_leaf(i)
            list_of_leaves.append(a)

def calling_fn():
    result = [] 
    get_leaves(rootnode, list_of_leaves=result)
    print result

Python对象总是通过引用传递。这已在here之前讨论过。一些内置类型是不可变的(例如intstring),因此您无法在适当的位置修改它们(当您连接两个字符串并将它们分配给变量时会创建一个新字符串)。可以在适当的位置修改可变类型的实例(例如list)。我们通过在递归调用中传递原始列表来累积结果来利用这一点。

要在真实应用程序中从HTML中提取文本,使用像BeautifulSouplxml.html这样的成熟库总是一个更好的选择(正如其他人所建议的那样)。

答案 1 :(得分:8)

如果将get_leaves()转换为生成器,则无需将累加器传递给函数或通过全局名称访问它:

def get_leaves(node):
    for child in getchildren(node):
        if leafnode(child):
            for each in get_leaves(child):
                yield each
        else:
            yield process_leaf(child)

def calling_fn():
    list_of_leaves = list(get_leaves(rootnode))
    print list_of_leaves

答案 2 :(得分:2)

使用像BeautifulSoup这样不错的HTML解析器,而不是尝试比现有软件更聪明。

答案 3 :(得分:2)

@pillmincher的生成器答案是最好的,但作为另一种选择,你可以将你的功能变成一个类:

class TagFinder:
    def __init__(self):
        self.leaves = []

    def get_leaves(self, node):
        kids = getchildren(node)
        for i in kids:
            if leafnode(i):
                self.get_leaves(i)
            else:
                a = process_leaf(i)
                self.list_of_leaves.append(a)
def calling_fn():
    finder = TagFinder()
    finder.get_leaves(rootnode)
    print finder.list_of_leaves

您的代码可能还涉及许多辅助函数,例如leafnode,因此一个类也可以帮助将它们组合成一个单元。

答案 4 :(得分:0)

作为关于递归的一般问题,这是一个很好的问题。通常有一个递归函数将数据累积到某个集合中。要么集合需要是全局变量(坏),要么将传递给递归函数。当几乎每种语言都传递集合时,只传递一个引用,因此您不必担心空间。有人刚刚发布了一个答案,显示了如何做到这一点。