【问题标题】:pythonic tree enumerator method using generator使用生成器的pythonic树枚举器方法
【发布时间】:2011-10-16 18:05:46
【问题描述】:

我实际上正在使用 Jython,并且对 Python 的处理方式非常陌生......

当我使用 javax.swing.tree.DefaultMutableTreeNode 时,我可以简单地去 depth/breadthFirstEnumeration()...

但是,如果我正在使用 DOM 树(例如来自 XML)做事情,那么就没有这样的等价物了……但让我感到震惊的是,在 Python/Jython 中必须有一种非常优雅和强大的方法来使用递归生成器。

希望我想要的是实用方法的最通用目的,它基本上可以使用任何类型的树对象进行枚举,你可以扔给它......所以你可能必须提供给你一个孩子的方法给定节点...在 org.w3c.dom.Node 的情况下,这将是 getChildNodes()... 然后您可能需要第二个可选参数来指定深度或广度...

令我惊讶的是,例如,我无法通过谷歌搜索或查看此处找到简单的答案。

【问题讨论】:

    标签: python recursion tree generator jython


    【解决方案1】:

    AFAIK,没有内置的实现。一个非常直接的解决方案是:

    import collections
    
    def depth_first_search(node, get_children, depth=0):
        yield node, depth
        for child in get_children(node):
            # In the upcoming Python 3.3, the following can be written as
            # yield from depth_first_search(child, get_children, depth + 1)
            for n, d in depth_first_search(child, get_children, depth + 1):
                yield n, d
    
    def breadth_first_search(node, get_children, depth=0):
        queue = collections.deque([(node, depth)])
        while queue:
            node, depth = queue.popleft()
            queue.extend((n, depth + 1) for n in get_children(node))
            yield node, depth
    

    然后你可以很容易地使用这些:

    def dom_get_children(node):
        nodeList = node.getNodeList()
        for i in range(nodeList.getLength()):
            yield nodeList.item(i)
    
    for node, depth in depth_first_search(some_dom_element, dom_get_children):
        # do something
    

    【讨论】:

      【解决方案2】:

      谢谢...虽然我一直在等待,但我一直在努力自己解决..

      免责声明:我在费迪南德提出他出色答案的最终版本之前写了这篇文章

      实际上,您的解决方案似乎适用于由常规 Python 列表组成的树...不幸的是 org.w3c.dom.Node 特别“钝”... getChildNodes() 实际上生成了一个名为 NodeList 的对象,尽管显然是某种列表(Java Array),但它仍然是密封的,不会自省……特别是 dir() 会告诉你它的“childNodes”字段的类是“org.apache.xerces.dom.DeferredElementImpl “……我的经验是,任何以“Impl”结尾的东西玩起来都不会很有趣……

      因此,我显然找不到将方法作为参数传递并调用它的方法......即使使用更适合 Python 的类,我目前也不清楚如何调用作为参数传递的方法......反正...

      所以下面是我的 3 个产品,非常不言自明:1) 深度优先 2) 选择深度优先或广度优先 3) 相同但提供带有深度指示的东西(这样您就可以格式化打印输出例如)。 不幸的是,对于解决方案#3,我不得不创建一个新类,因为我发现不可能,例如,向 Node 对象添加一个属性……显然,与 Python 相比,Jython 有局限性和“杂质”。我知道有用于处理 XML 等的 python 模块......将在适当的时候研究它。 (请注意,当然,Jython 的一大优点是您可以逐步从 Java 过渡到 Python。

      如果有经验丰富的 Python/Jython 人有任何 cmets,我会很感兴趣...

      1. 仅深度优先:

        def depthFirstTreeEnumeration( node ):
          nodeList = node.getChildNodes()
          for i in range( nodeList.getLength()):
            childNode = nodeList.item( i )
            yield childNode
            for item in depthFirstTreeEnumeration( childNode ):
              yield item
        
      2. 选择深度优先还是广度优先

        def treeEnumeration( node, depthFirst = True ):
          nodeList = node.getChildNodes()
          for i in range( nodeList.getLength()):
            childNode = nodeList.item( i )
            yield childNode
            if depthFirst:
              for item in treeEnumeration( childNode ):
                yield item
          if not depthFirst:
            for i in range( nodeList.getLength()):
              childNode = nodeList.item( i )
              for item in treeEnumeration( childNode, False ):
                yield item
        
      3. 选择深度优先或广度优先,并指示给定节点的深度

        class NodeWrapper():
          def __init__(self, node, depth ):
            self.node = node
            self.depth = depth
          def __repr__( self ):
            return "node %s, depth %d" % (self.node, self.depth)
        
        def treeEnumerationShowDepth( node, depth = 0, depthFirst = True ):
          nodeList = node.getChildNodes()
          for i in range( nodeList.getLength()):
            wrapper = NodeWrapper( nodeList.item( i ), depth )
            yield wrapper
            if depthFirst:
              for item in treeEnumerationShowDepth( wrapper.node, depth + 1 ):
                yield item
          if not depthFirst:
            for i in range( nodeList.getLength()):
              childNode = nodeList.item( i )
              for item in treeEnumerationShowDepth( childNode, depth + 1, False ):
                yield item
        
        from org.w3c.dom import Node
        
        for wrapper in treeEnumerationShowDepth( dom.getDocumentElement(), 0, False ):
          print "%snode: %s" % ( wrapper.depth * "  ", wrapper.node )
        

      【讨论】:

      • 我的实现确实需要列表,但可以处理任何 iterable 对象。您可以使用range()yield 生成nodeList.item() 的结果轻松编写NodeList 特定get_children 生成器,然后使用我建议的通用实现。而不是你的包装,只需扩展搜索功能以采用depth 参数,产生对(node, depth) 而不仅仅是节点,并将depth + 1 传递给递归调用。然后你可以这样做:for node, depth in ....
      • ...我的意思是“not 需要列表”,当然。请参阅我的更新答案。
      猜你喜欢
      • 2021-06-02
      • 1970-01-01
      • 2017-03-02
      • 2017-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-04
      相关资源
      最近更新 更多