3

总之

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

输入数据

在一个名为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)
4

3 回答 3

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)
于 2012-11-17T18:13:21.990 回答
1

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

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

于 2012-11-17T18:13:10.563 回答
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)
于 2012-11-17T18:13:31.760 回答