使用发生器的pythonic树枚举器方法

时间:2011-10-16 18:05:47

标签: python recursion tree generator jython

我实际上正在使用Jython,而且我对Python的做法很新......

当我使用javax.swing.tree.DefaultMutableTreeNode时,我可以直接使用depth / breadthFirstEnumeration()......

但是,如果我正在使用DOM树(例如来自XML),那么就没有这样的等价物......但是我觉得必须有一种非常优雅和强大的方法在Python / Jython中使用递归发生器。

希望我想要的是实用程序方法的最一般目的,它实际上会对你可以抛出的任何类型的树对象进行枚举...所以你可能必须提供给你的孩子的方法给定节点...在org.w3c.dom.Node的情况下,这将是getChildNodes()...那么你可能需要第二个可选的param来指定深度或广度......

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

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

谢谢...虽然我一直在等待,但我一直在努力解决这个问题。

免责声明:我在Ferdinand提出他的优秀答案的最终版本之前写过这篇文章

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

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

以下是我的3个产品,非常不言自明:1)深度优先2)深度或宽度优先选择3)相同但提供深度指示的东西(因此您可以格式化打印输出例如)。 对于解决方案#3,遗憾的是我被迫创建了一个新类,因为我发现例如向Node对象添加一个属性是不可能的......显然Jython与Python相比具有局限性和“杂质”。我知道有处理XML等的python模块......将在适当的时候调查它。 (当然,注意Jython的一个很好的方面是你可以将逐渐从Java过渡到Python)。

如果有经验的Python / Jython人有任何评论,那么会感兴趣吗...

  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 )
    
相关问题