lxml的迭代器的奇怪行为

时间:2015-08-17 09:49:45

标签: python iterator lxml

任务是 - 在迭代xml树时删除'当前'节点,保存xml文档并将其提供给第三方应用程序。根据结果​​将此节点带回树或忘记它。

让我展示一下奇怪之处:<​​/ p>

<test>
  <A>
    <A1>
      <A2>A2</A2>
    </A1>
  </A>
  <B>
    <B1>B1</B1>
  </B>
  <C>C</C>
</test>

这是python代码:

from lxml import etree as ET

tree = ET.parse('t.xml')

delete = False

def print_tree():
    print '*' * 5
    for node in tree.getiterator():
        print node.tag
    print '*' * 5

print_tree()

for node in tree.getiterator():
    #delete the first node (<A> in our case)
    if not delete:
        try:
            node.getparent().remove(node)
            delete = True
        except:
            pass

    print '* ' + node.tag

print_tree()

输出将是这样的:

*****
test
A
A1
A2
B
B1
C
***** <-- these are all elements iterator can reach
* test
* A
* A1
* A2
*****
test
B
B1
C
*****

正如您在删除节点后所看到的那样,迭代器仅转到A分支。

如何让它覆盖树的其余部分?我希望有一个更优雅的解决方案。

1 个答案:

答案 0 :(得分:1)

我认为您的代码或环境存在多个问题。

当我运行你的代码(Windows 7x64 32位python 2.7.8)时,我得到以下(与你的不同)输出:

*****
test
A
A1
A2
B
B1
C
*****
* test
* A
* A1
* A2
*****
test
B
B1
C
*****

所以我从你那里获得不同输出的第一个问题可能是环境 - 你的或我的。您使用的是什么版本的python?

你的问题是为什么迭代器不进入树的B部分?好吧,看看你的删除代码,它删除了当前节点,然后你假设迭代器将继续进一步迭代进入树的其余部分 - 即你正在修改当前正在运行的点上的树。这很可能会混淆迭代器,它正在做。

AFAICT remove()方法的描述说'从元素中删除子元素'。您正在尝试使用删除删除“元素”,这就像坐在树枝的末端并在树干附近锯开它。

假设您只想使用迭代器删除A节点,这是有效的(请注意中断 - 进一步迭代没有意义)(还要注意没有try / except):

for node in tree.getiterator():
    #delete the first <A> subelement
    Anode = node.find("A")
    if Anode is not None:
        node.remove(Anode)
        break

另一个问题可能在于您的代码。你的except语句只包含'pass'来抑制异常 - 这是一个非常狡猾的事情。我的环境中的异常原因是第一次进入for循环,即在根节点测试时,对getparent()的调用正确地返回None,并且无法删除。抑制这样的所有异常并不是解决该问题的可靠方法,因为它还可以抑制任何其他错误,并且无论如何意味着代码中可能存在逻辑错误。

HTH 巴尼