2011-01-18 2 views
4

Стандартная реализация ElementTree для Python (2.6) не предоставляет указатели родителям из дочерних узлов. Поэтому, если родители необходимы, предлагается зацикливать родителей, а не детей.Итерация нескольких (родительских, дочерних) узлов с использованием Python ElementTree

Рассмотрим мой XML имеет вид:

<Content> 
    <Para>first</Para> 
    <Table><Para>second</Para></Table> 
    <Para>third</Para> 
</Content> 

Следующая находит все "Para" узлов без учета родителей:

(1) paras = [p for p in page.getiterator("Para")] 

Это (адаптировано из effbot) хранит родительский обернув над их вместо дочерних узлов:

(2) paras = [(c,p) for p in page.getiterator() for c in p] 

Это имеет смысл и может быть расширенным д с условным для достижения (предположительно) же результата, как (1), но с родительской информацией добавлено:

(3) paras = [(c,p) for p in page.getiterator() for c in p if c.tag == "Para"] 

ElementTree documentation предполагает, что метод getiterator() делает поиск в глубине. Запуск без ищет родителя (1) дает:

first 
second 
third 

Однако извлечение текста из пунктов в (3), выходы:

first, Content>Para 
third, Content>Para 
second, Table>Para 

Это по-видимому, в ширину.

В связи с этим возникает два вопроса.

  1. Это правильное и ожидаемое поведение?
  2. Как вы извлекаете (родительский, дочерний) кортежи, когда ребенок должен иметь определенный тип, но родитель может быть чем угодно, , если заказ документа должен быть сохранен. Я не думаю, что запуск двух циклов и сопоставление (родительский, дочерний), сгенерированный (3), с порядками, сгенерированными (1), является идеальным.

ответ

5

Рассмотрим это:

>>> xml = """<Content> 
... <Para>first</Para> 
... <Table><Para>second</Para></Table> 
... <Para>third</Para> 
... </Content>""" 
>>> import xml.etree.cElementTree as et 
>>> page = et.fromstring(xml) 
>>> for p in page.getiterator(): 
...  print "ppp", p.tag, repr(p.text) 
...  for c in p: 
...   print "ccc", c.tag, repr(c.text), p.tag 
... 
ppp Content '\n ' 
ccc Para 'first' Content 
ccc Table None Content 
ccc Para 'third' Content 
ppp Para 'first' 
ppp Table None 
ccc Para 'second' Table 
ppp Para 'second' 
ppp Para 'third' 
>>> 

Помимо: списочные великолепны, пока вы не хотите, чтобы увидеть именно то, что итерации :-)

getiteratorявляется приносящему «РРР» элементы в объявленном порядке. Однако вы выщипываете свои элементы из вспомогательных элементов «ccc», которые не находятся в вашем желаемом порядке.

Одно из решений, чтобы сделать свой собственный итерацию:

>>> def process(elem, parent): 
... print elem.tag, repr(elem.text), parent.tag if parent is not None else None 
... for child in elem: 
...  process(child, elem) 
... 
>>> process(page, None) 
Content '\n ' None 
Para 'first' Content 
Table None Content 
Para 'second' Table 
Para 'third' Content 
>>> 

Теперь вы можете Snarf элементы «Para» каждый с ссылкой на своего родителя (если таковые имеются), поскольку они потоковую передачу мимо.

Это может быть обернуты красиво в генераторе гаджет:

>>> def iterate_with_parent(elem): 
...  stack = [] 
...  while 1: 
...   for child in reversed(elem): 
...    stack.append((child, elem)) 
...   if not stack: return 
...   elem, parent = stack.pop() 
...   yield elem, parent 
... 
>>> 
>>> showtag = lambda e: e.tag if e is not None else None 
>>> showtext = lambda e: repr((e.text or '').rstrip()) 
>>> for e, p in iterate_with_parent(page): 
...  print e.tag, showtext(e), showtag(p) 
... 
Para 'first' Content 
Table '' Content 
Para 'second' Table 
Para 'third' Content 
>>> 
+0

Спасибо за это решение. знак равно – mmmdreg

Смежные вопросы