xml.etree.ElementTree.Element.remove 未删除所有元素

jam*_*gni 2 python xml elementtree

请看下面的代码:

import xml.etree.ElementTree as ET
for x in ("<a><b /><c><d /></c></a>", "<a><q /><b /><c><d /></c></a>", "<a><m /><q /><b /><c><d /></c></a>"):
    root = ET.fromstring(x)
    for e in root: root.remove(e)
    print(ET.tostring(root))
Run Code Online (Sandbox Code Playgroud)

我希望它在所有情况下都能输出<a></a>,但它给出了:

b'<a><c><d /></c></a>'
b'<a><b /></a>'
b'<a><q /><c><d /></c></a>'
Run Code Online (Sandbox Code Playgroud)

我完全不明白这一点。我也没有看到被删除的特定元素有任何模式。

该文档只是说:

从元素中删除子元素。与 find* 方法不同,此方法根据实例标识而不是标记值或内容来比较元素。

我在做什么/假设错了?我在 Kubuntu Trusty 上使用 Python 2.7.5 和 3.4.0 得到的输出基本相同。

谢谢!

mha*_*wke 5

这说明了这个问题:

>>> root = ET.fromstring("<a><b /><c><d /></c></a>")
>>> for e in root:
...     print(e)
... 
<Element 'b' at 0x7f76c6d6cd18>
<Element 'c' at 0x7f76c6d6cd68>
>>> for e in root:
...     print(e)
...     root.remove(e)
...
<Element 'b' at 0x7f76c6d6cd18>
Run Code Online (Sandbox Code Playgroud)

因此,修改正在迭代的对象会影响迭代。这并不完全出乎意料,如果您在迭代列表时更改列表,情况是一样的:

>>> l = [1, 2, 3, 4]
>>> for i in l:
...     l.remove(i)
>>> print l
[2, 4]
Run Code Online (Sandbox Code Playgroud)

作为解决方法,您可以重复删除第一个子元素,如下所示:

import xml.etree.ElementTree as ET
for x in ("<a><b /><c><d /></c></a>", "<a><q /><b /><c><d /></c></a>", "<a><m /><q /><b /><c><d /></c></a>"):
    root = ET.fromstring(x)
    for i in range(len(root)):
        root.remove(root[0])
    ET.tostring(root)
Run Code Online (Sandbox Code Playgroud)

输出

b'<a />'
b'<a />'
b'<a />'
Run Code Online (Sandbox Code Playgroud)

这是可行的,因为在执行循环时迭代器不会变化。或者,如果您想删除根元素的所有子元素及其所有属性,您可以使用root.clear()

>>> root = ET.fromstring('<a href="blah"><b /><c><d /></c></a>')
>>> root.clear()
>>> ET.tostring(root)
b'<a />'
Run Code Online (Sandbox Code Playgroud)