在递归函数中处理Python全局变量

4 投票
5 回答
4925 浏览
提问于 2025-04-16 21:19

我写了一个程序,可以从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。调用函数调用了这个变量,而 get_leaves() 则往这个变量里添加内容。

我的问题是,我该如何修改我的函数,以便能够像这样使用 list_of_leaves=get_leaves(rootnode),也就是说不使用全局变量?

我不想让每次调用这个函数时都复制一份列表,因为这个列表可能会变得很大。

请不要批评这个伪代码的设计,因为我简化了它。它是为了另一个目的:使用 BeautifulSoup 提取带有相关标签的标记。

5 个回答

2

用一个好用的HTML解析工具,比如BeautifulSoup,别想着自己比现有的软件更聪明。

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
10

你可以把结果列表作为一个可选的参数传递。

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中,所有对象都是通过引用传递的。这一点之前已经讨论过了,具体可以查看这里。有些内置类型是不可变的,比如 int(整数)和 string(字符串),所以你不能直接修改它们(比如,当你把两个字符串拼接在一起并赋值给一个变量时,实际上是创建了一个新的字符串)。而可变类型的实例,比如 list(列表),是可以直接修改的。我们利用这一点,在递归调用中传递原始列表来累积结果。

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

撰写回答