【问题标题】:Refactoring to eliminate global variable in recursive function重构以消除递归函数中的全局变量
【发布时间】:2012-11-06 03:58:34
【问题描述】:

总结

我正在使用来自 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)

【问题讨论】:

    标签: python recursion


    【解决方案1】:

    您的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)
    

    【讨论】:

    • 感谢 Martijn -- 我接受了这个解决方案。我可以就我的补充问题发表评论吗?我正在寻找一种优雅的方式来检索 tree 一旦它被创建并且递归完成。
    • @gauden:我们通常会尝试将其保留在每个帖子一个问题上。我会说在这种情况下扔掉递归函数的返回值之一是可以的,是的。或者,使用一个类来保存状态,然后在最后从您使用的类实例中检索构建的树。
    • 谢谢@martijn-pieters。我已经有足够长的时间知道规则了,所以很抱歉这次越界了。
    • @gauden:你走运了;无论如何,我已经给了你一个基于类的版本,这使它更加 Python 并且给你树作为一个易于访问的属性。
    • 谢谢,非常感谢:)
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      在你的函数中使用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)
      

      【讨论】:

      • 啊,我认为给出使用可变变量作为默认值的建议是这里的问题,也许? :-) 你肯定熟悉"Least Astonishment" in Python: The Mutable Default Argument
      • @MartijnPieters.. 但是,这就是我们所需要的。为什么在这里很重要。 tree 在连续通话中发生变化。仍然无法理解为什么要投反对票。 :(
      • @MartijnPieters.. 是的,我知道。但是,如果我们从外部递归多次调用该方法,那将很重要。在递归中,这有关系吗?我认为不应该。
      • 您现在只能使用此功能(来自main()一次。下次你调用它时,它会保留上次调用的数据。
      • @MartijnPieters .. 是的,这可能是个问题,我理解。我想我会把它改成None
      猜你喜欢
      • 1970-01-01
      • 2020-10-02
      • 2015-04-23
      • 1970-01-01
      • 2015-03-08
      • 2020-07-29
      • 2017-10-03
      • 1970-01-01
      • 2022-01-07
      相关资源
      最近更新 更多