xml.etree.cElementTree
viene cerca de un generador con el uso correcto; de forma predeterminada, recibe cada elemento después de su evento 'final', en cuyo punto puede procesarlo. Debe usar element.clear() en el elemento si no lo necesita después del procesamiento; así ahorras la memoria.
Aquí hay un ejemplo completo de lo que quiero decir, donde analizo la biblioteca de Rhythmbox (reproductor de música). Utilizo iterparse de (c) ElementTree y para cada elemento procesado llamo element.clear() para que guarde una gran cantidad de memoria. (Por cierto, el siguiente código es un sucesor de algún código sax para hacer lo mismo, la solución cElementTree fue un alivio ya que 1) El código es conciso y expresa lo que necesito y nada más 2) Es 3 veces más rápido, 3) que utiliza menos memoria.)
import os
import xml.etree.cElementTree as ElementTree
NEEDED_KEYS= set(("title", "artist", "album", "track-number", "location",))
def _lookup_string(string, strmap):
"""Look up @string in the string map,
and return the copy in the map.
If not found, update the map with the string.
"""
string = string or ""
try:
return strmap[string]
except KeyError:
strmap[string] = string
return string
def get_rhythmbox_songs(dbfile, typ="song", keys=NEEDED_KEYS):
"""Return a list of info dictionaries for all songs
in a Rhythmbox library database file, with dictionary
keys as given in @keys.
"""
rhythmbox_dbfile = os.path.expanduser(dbfile)
lSongs = []
strmap = {}
# Parse with iterparse; we get the elements when
# they are finished, and can remove them directly after use.
for event, entry in ElementTree.iterparse(rhythmbox_dbfile):
if not (entry.tag == ("entry") and entry.get("type") == typ):
continue
info = {}
for child in entry.getchildren():
if child.tag in keys:
tag = _lookup_string(child.tag, strmap)
text = _lookup_string(child.text, strmap)
info[tag] = text
lSongs.append(info)
entry.clear()
return lSongs
Ahora, no entiendo sus expectativas, ¿tiene la siguiente expectativa?
# take one
for event, entry in ElementTree.iterparse(rhythmbox_dbfile):
# parse some entries, then exit loop
# take two
for event, entry in ElementTree.iterparse(rhythmbox_dbfile):
# parse the rest of entries
¡Cada vez que llame a iterparse obtendrá un nuevo objeto iterador, leyendo el archivo de nuevo! Si desea que un objeto persistente con la semántica de iterador, hay que hacer referencia al mismo objeto en ambos bucles (código no probado):
#setup
parseiter = iter(ElementTree.iterparse(rhythmbox_dbfile))
# take one
for event, entry in parseiter:
# parse some entries, then exit loop
# take two
for event, entry in parseiter:
# parse the rest of entries
creo que puede ser confuso ya que los diferentes objetos tienen una semántica diferente. Un objeto de archivo siempre tendrá un estado interno y avance en el archivo, independientemente de cómo lo itere. Un objeto Iterparse ElementTree aparentemente no. Lo crucial es pensar que cuando usas un ciclo for, el for siempre llama a iter() en el objeto sobre el que iteras. Aquí es un experimento comparando ElementTree.iterparse con un objeto de archivo:
>>> import xml.etree.cElementTree as ElementTree
>>> pth = "/home/ulrik/.local/share/rhythmbox/rhythmdb.xml"
>>> iterparse = ElementTree.iterparse(pth)
>>> iterparse
<iterparse object at 0x483a0890>
>>> iter(iterparse)
<generator object at 0x483a2f08>
>>> iter(iterparse)
<generator object at 0x483a6468>
>>> f = open(pth, "r")
>>> f
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98>
>>> iter(f)
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98>
>>> iter(f)
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98>
Lo que se ve es que cada llamada a iter() sobre un objeto iterparse devuelve un nuevo generador. Sin embargo, el objeto de archivo tiene un estado del sistema operativo interno que debe conservarse y es su propio iterador.
eso es lo que quiero ... No me importa tener que "reaccionar" a eventos como "etiqueta de inicio", etc. – jldupont
@ Jean-Lou: si no necesitas todo el árbol, entonces SAX es el camino a seguir. Está hecho para procesar documentos como una secuencia de eventos en lugar de un árbol de contenido. –