2010-11-08 5 views
11

Ich habe die folgende Funktion, die eine grobe Arbeit der Analyse einer XML-Datei in ein Wörterbuch tut.Wie kann diese Funktion neu geschrieben werden, um OrderedDict zu implementieren?

Leider, da Python-Wörterbücher nicht geordnet sind, kann ich nicht durch die Knoten, wie ich möchte.

Wie ändere ich das, so dass es ein geordnetes Wörterbuch ausgibt, das die ursprüngliche Reihenfolge der Knoten widerspiegelt, wenn es mit 'for' geloopt wird.

def simplexml_load_file(file): 
    import collections 
    from lxml import etree 

    tree = etree.parse(file) 
    root = tree.getroot() 

    def xml_to_item(el): 
     item = None 
     if el.text: 
      item = el.text 
     child_dicts = collections.defaultdict(list) 
     for child in el.getchildren(): 
      child_dicts[child.tag].append(xml_to_item(child)) 
     return dict(child_dicts) or item 

    def xml_to_dict(el): 
     return {el.tag: xml_to_item(el)} 

    return xml_to_dict(root) 

x = simplexml_load_file('routines/test.xml') 

print x 

for y in x['root']: 
    print y 

Ausgänge:

{'root': { 
    'a': ['1'], 
    'aa': [{'b': [{'c': ['2']}, '2']}], 
    'aaaa': [{'bb': ['4']}], 
    'aaa': ['3'], 
    'aaaaa': ['5'] 
}} 

a 
aa 
aaaa 
aaa 
aaaaa 

Wie kann ich collections.OrderedDict implementieren, so dass ich sicher sein kann, die richtige Reihenfolge der Knoten zu bekommen?

XML-Datei als Referenz:

<root> 
    <a>1</a> 
    <aa> 
     <b> 
      <c>2</c> 
     </b> 
     <b>2</b> 
    </aa> 
    <aaa>3</aaa> 
    <aaaa> 
     <bb>4</bb> 
    </aaaa> 
    <aaaaa>5</aaaaa> 
</root> 
+0

Duplikat http zu tun: // Stackoverflow. com/questions/4123266/python-schleifen-scheint-nicht-folgen-reihenfolge vom selben autor. –

Antwort

27

Sie könnten die neue Unterklasse OrderedDictdict verwenden, die zum Modul collections der Standardbibliothek in Version 2.7 * hinzugefügt wurde. Eigentlich, was Sie brauchen, ist eine Ordered + defaultdict Kombination, die nicht existiert, aber es ist möglich, eine zu schaffen, indem Subklassen OrderedDict wie unten dargestellt:

import collections 

class OrderedDefaultdict(collections.OrderedDict): 
    """ A defaultdict with OrderedDict as its base class. """ 

    def __init__(self, default_factory=None, *args, **kwargs): 
     if not (default_factory is None 
       or isinstance(default_factory, collections.Callable)): 
      raise TypeError('first argument must be callable or None') 
     super(OrderedDefaultdict, self).__init__(*args, **kwargs) 
     self.default_factory = default_factory # called by __missing__() 

    def __missing__(self, key): 
     if self.default_factory is None: 
      raise KeyError(key,) 
     self[key] = value = self.default_factory() 
     return value 

    def __reduce__(self): # optional, for pickle support 
     args = (self.default_factory,) if self.default_factory else tuple() 
     return self.__class__, args, None, None, self.iteritems() 

    def __repr__(self): # optional 
     return '%s(%r, %r)' % (self.__class__.__name__, self.default_factory, 
           list(self.iteritems())) 

def simplexml_load_file(file): 
    from lxml import etree 

    tree = etree.parse(file) 
    root = tree.getroot() 

    def xml_to_item(el): 
     item = el.text or None 
     child_dicts = OrderedDefaultdict(list) 
     for child in el.getchildren(): 
      child_dicts[child.tag].append(xml_to_item(child)) 
     return collections.OrderedDict(child_dicts) or item 

    def xml_to_dict(el): 
     return {el.tag: xml_to_item(el)} 

    return xml_to_dict(root) 

x = simplexml_load_file('routines/test.xml') 
print(x) 

for y in x['root']: 
    print(y) 

Die Ausgabe von Ihrem Test XML-Datei wie folgt aussieht produziert:

Ausgang:

{'root': 
    OrderedDict(
     [('a', ['1']), 
     ('aa', [OrderedDict([('b', [OrderedDict([('c', ['2'])]), '2'])])]), 
     ('aaa', ['3']), 
     ('aaaa', [OrderedDict([('bb', ['4'])])]), 
     ('aaaaa', ['5']) 
     ] 
    ) 
} 

a 
aa 
aaa 
aaaa 
aaaaa 

was ich denke, ist in der Nähe zu dem, was Sie wollen.

* Wenn Ihre Version von Python nicht über OrderedDict verfügt, das in Version 2.5 eingeführt wurde, können Sie stattdessen das ActiveState-Rezept Ordered Dictionary for Py2.4 von Raymond Hettinger als Basisklasse verwenden.

Minor Update:

Added ein __reduce__() Verfahren, das die Instanzen der Klasse ermöglicht werden gebeizt und ungebeizt richtig. Dies war für diese Frage nicht notwendig, kam aber in similar ein.

1

Es gibt viele mögliche Implementierung von OrderedDict in der Antwort aufgeführt hier: How do you retrieve items from a dictionary in the order that they're inserted?

Sie können Ihr eigenes OrderedDict Modul zur Verwendung in Ihrem eigenen Code erstellen, indem man das Kopieren der Implementierungen. Ich nehme an, dass Sie aufgrund der von Ihnen ausgeführten Version von Python keinen Zugriff auf OrderedDict haben.

Ein interessanter Aspekt Ihrer Frage ist die mögliche Notwendigkeit für defaultdict Funktionalität. Wenn Sie dies benötigen, können Sie die Methode __missing__ implementieren, um den gewünschten Effekt zu erzielen.

1

Das Rezept von Martineau funktioniert für mich, aber es hat Probleme mit der Methode copy() geerbt von DefaultDict.Der folgende Ansatz behebt diesen Nachteil:

class OrderedDefaultDict(OrderedDict): 
    #Implementation as suggested by martineau 

    def copy(self): 
     return type(self)(self.default_factory, self) 

Bitte beachten Sie, dass diese Implementierung keine deep tut, die für Standard-Wörterbücher eher das Richtige besonders scheint in den meisten Fällen