admirabilis
admirabilis

Reputation: 2420

lxml.etree._Element.append() from a loop not working as expected

I would like to know why in this code append() seems to work from inside the loop, but the resulting xml displays the modification from only the last iteration, while remove() works as expected. This is a overly simplified example, I'm working with big chunks of data, and need to append the same subtree to many different parents.

from lxml import etree

xml = etree.fromstring('<tree><fruit id="1"></fruit><fruit id="2"></fruit></tree>')
sub = etree.fromstring('<apple/>')

for i, item in enumerate(xml):
    item.append(sub)
    print('Fruit {} with sub appended: {}'.format(
        i, etree.tostring(item).decode('ascii')))

print('\nResulting tree after iterating through items with append():\n' +
    etree.tostring(xml, pretty_print=True).decode('ascii'))

for item in xml:
    xml.remove(item)

print('Resulting tree after iterating through items with remove():\n' +
    etree.tostring(xml, pretty_print=True).decode('ascii'))

Current output:

Fruit 0 with sub appended: <fruit id="1"><apple/></fruit>
Fruit 1 with sub appended: <fruit id="2"><apple/></fruit>

Resulting tree after iterating through items with append():
<tree>
  <fruit id="1"/>
  <fruit id="2">
    <apple/>
  </fruit>
</tree>

Resulting tree after iterating through items with remove():
<tree/>

Expected output from after iterating through items with append():

<tree>
  <fruit id="1"/>
    <apple/>
  </fruit>
  <fruit id="2">
    <apple/>
  </fruit>
</tree>

Upvotes: 3

Views: 1216

Answers (1)

har07
har07

Reputation: 89295

That's because you only created one instance of <apple/> to be appended. So basically you just moved that one instance from one parent to another until the last append(sub) executed. Try to move creation of the <apple/> element within for loop instead :

for i, item in enumerate(xml):
    sub = etree.fromstring('<apple/>')
    item.append(sub)
    print('Fruit {} with sub appended: {}'.format(
        i, etree.tostring(item).decode('ascii')))
print()

output :

Resulting tree after iterating through items with append():
<tree>
  <fruit id="1">
    <apple/>
  </fruit>
  <fruit id="2">
    <apple/>
  </fruit>
</tree>

Upvotes: 1

Related Questions