重构以消除递归函数中的全局变量

时间:2012-11-17 18:11:02

标签: python recursion

摘要

我正在使用来自SO问题:Python file parsing: Build tree from text file的函数从文本文件输入中创建树结构。但是我只能通过使用全局变量来生成我的树,并且无法找到避免这种情况的方法。

输入数据

在名为data.txt的文件中,我有以下内容:

Root
-A 10
-B
--B A 2
--B B 5
--B Z 9
--B X
---X 4
---Y
----Y0 67
----Y1 32
---Z 3
-C 19

期望结果

{'B': ['B A 2', 'B B 5', 'B Z 9', 'B X'],
 'B X': ['X 4', 'Y', 'Z 3'],
 'Root': ['A 10', 'B', 'C 19'],
 'Y': ['Y0 67', 'Y1 32']}

我的代码

import re, pprint
PATTERN = re.compile('^[-]+')
tree = {}

def _recurse_tree(parent, depth, source):
    last_line = source.readline().rstrip()
    while last_line:
        if last_line.startswith('-'):
            tabs = len( re.match(PATTERN, last_line).group() )
        else:
            tabs = 0
        if tabs < depth:
            break
        node = re.sub(PATTERN, '', last_line.strip())
        if tabs >= depth:
            if parent is not None:
                print "%s: %s" %(parent, node)
                if parent in tree:
                    tree[parent].append(node)
                else:
                    tree[parent] = [ node, ]
            last_line = _recurse_tree(node, tabs+1, source)
    return last_line

def main():
    inFile = open("data.txt")
    _recurse_tree(None, 0, inFile)
    pprint.pprint(tree)

if __name__ == "__main__":
    main()

问题

如何摆脱全局变量tree?我所做的一切似乎都使代码更长或更丑,但我想大量使用这个功能,我讨厌取决于核心结果的副作用。

补编

在下面的答案之后,我修改了代码,以下列方式返回tree。这是pythonic吗?返回一个元组,然后抛出第一个元素似乎不够优雅。

def _recurse_tree(parent, depth, source, tree=None):
    if tree is None:
        tree = {}
    last_line = source.readline().rstrip()
    while last_line:
        if last_line.startswith('-'):
            tabs = len( re.match(PATTERN, last_line).group() )
        else:
            tabs = 0
        if tabs < depth:
            break
        node = re.sub(PATTERN, '', last_line.strip())
        if tabs >= depth:
            if parent is not None:
                print "%s: %s" %(parent, node)
                if parent in tree:
                    tree[parent].append(node)
                else:
                    tree[parent] = [ node, ]
            last_line, tree = _recurse_tree(node, tabs+1, source, tree)
    return last_line, tree

def main():
    inFile = open("data.txt")
    tmp, tree = _recurse_tree(None, 0, inFile)
    pprint.pprint(tree)

3 个答案:

答案 0 :(得分:4)

您的tree变量已经是可变的;只需将它与递归调用一起传递:

def _recurse_tree(parent, depth, source, tree=None):
    if tree is None:
        tree = {}

    last_line = source.readline().rstrip()
    while last_line:
        if last_line.startswith('-'):
            tabs = len( re.match(PATTERN, last_line).group() )
        else:
            tabs = 0
        if tabs < depth:
            break
        node = re.sub(PATTERN, '', last_line.strip())
        if tabs >= depth:
            if parent is not None:
                print "%s: %s" %(parent, node)
                if parent in tree:
                    tree[parent].append(node)
                else:
                    tree[parent] = [ node, ]
            last_line = _recurse_tree(node, tabs+1, source, tree)
    return last_line

或者,您可以使用类来保存状态,然后从实例中获取状态会更容易:

class TreeBuilder(object):
    _PATTERN = re.compile('^[-]+')

    def __init__(self, source):
        self.tree = {}
        self.source = source
        self._recurse_tree()

    def _recurse_tree(self, parent=None, depth=0):
         last_line = self.source.readline().rstrip()
         while last_line:
             if last_line.startswith('-'):
                 tabs = len( self._PATTERN.match(last_line).group() )
             else:
                 tabs = 0
             if tabs < depth:
                 break
             node = self._PATTERN.sub('', last_line.strip())
             if tabs >= depth:
                 if parent is not None:
                     print "%s: %s" %(parent, node)
                     if parent in self.tree:
                         self.tree[parent].append(node)
                     else:
                         self.tree[parent] = [ node, ]
                 last_line = self._recurse_tree(node, tabs+1)
         return last_line

然后像这样使用:

def main():
    inFile = open("data.txt")
    builder = TreeBuilder(inFile)
    pprint.pprint(builder.tree)

答案 1 :(得分:1)

我认为这里的解决方案很好 - 创建类并将树放入内部,就像私有类成员一样。

或者您可以简单地将此字典作为函数中的一个参数传递,并在递归期间传递它。它将通过引用传递,因此所有时间函数将使用没有全局变量的相同字典。但我更喜欢上课。

答案 2 :(得分:0)

在您的函数中使用tree作为默认参数: -

def _recurse_tree(parent, depth, source, tree = None):
    if tree is None:  # needed on first invocation
        tree = {}

在没有tree参数的情况下第一次调用它,并在每次连续调用时,向其添加另一个参数tree

因此,从您的方法内部,您的递归调用变为: -

last_line = _recurse_tree(node, tabs+1, source, tree)