2013-05-14 7 views
29

Warum wird mein Python OrderedDict initialisiert 'out of order'?Python OrderedDict Iteration

Die Lösung hier ist weniger faszinierend als die Erklärung. Es gibt etwas hier, das ich einfach nicht verstehe, und vielleicht würde eine Erweiterung anderen genauso helfen wie mir.

>>> from collections import OrderedDict 

>>> spam = OrderedDict(s = (1, 2), p = (3, 4), a = (5, 6), m = (7, 8)) 

>>> spam 
OrderedDict([('a', (5, 6)), ('p', (3, 4)), ('s', (1, 2)), ('m', (7, 8))]) 

>>> for key in spam.keys(): 
... print key  
... 
# this is 'ordered' but not the order I wanted.... 
a 
p 
s 
m 

# I was expecting (and wanting): 
s 
p 
a 
m 
+10

"kwargs" sind nicht bestellt. – georg

+0

Mögliches Duplikat von [Konvertierung von dict nach OrderedDict] (http://stackoverflow.com/questions/15711755/converting-dict-to-ordereddict) –

Antwort

36

Von the docs:

Der OrderedDict Konstruktor und update() Methode sowohl Stichwort Argumente akzeptieren, aber ihre Ordnung verloren, weil Python Funktionsaufruf Semantik Pass in Keyword-Argumenten ein reguläres ungeordnetes Wörterbuch verwenden.

So Initialisierung verliert die Reihenfolge, da es im Grunde einen Konstruktor mit **kwargs aufruft.

Edit: Im Sinne einer Lösung (nicht nur eine Erklärung) -wie in a comment by the OP wies darauf hin, in einer einzigen Liste von Tupeln vorbei wird Arbeit:

>>> from collections import OrderedDict 
>>> spam = OrderedDict([('s',(1,2)),('p',(3,4)),('a',(5,6)),('m',(7,8))]) 
>>> for key in spam: 
...  print(key) 
... 
s 
p 
a 
m 
>>> for key in spam.keys(): 
...  print(key) 
... 
s 
p 
a 
m 

Dies ist weil es nur ein einziges Argument, eine Liste, bekommt.

+5

Also das funktioniert so: OrderedDict ([('s', (1 , 2)), ('p', (3,4)), ('a', (5,6)), ('m', (7,8))] In diesem Fall erhält der Konstruktor ein einziges Argument, eine [list] danke –

+0

Dies ist mit Python 3.6 richtig gelöst? aus der docs - Geändert in Version 3.6: Mit der Annahme von PEP 468 bleibt Reihenfolge für Schlüsselwortargumente erhalten, die an den OrderedDict-Konstruktor und seine Aktualisierung übergeben() Methode – gpk27

+0

@ chris-krycho, die Out-of-Place-Reihenfolge wird später erhalten, richtig? – gpk27

17

@Chris Krycho gab eine gute Erklärung, warum die Dinge scheitern.

Wenn Sie sich die repr() eines OrderedDict ansehen, erhalten Sie einen Hinweis darauf, wie Sie von Anfang an Ordnung schaffen: Sie müssen eine Liste von (Schlüssel, Wert) Paaren verwenden, um die Reihenfolge der Schlüssel zu erhalten Die Liste.

Hier ist ein Ich habe früher:

>>> from collections import OrderedDict 
>>> spamher = OrderedDict(s=6, p=5, a=4, m=3, h=2, e=1, r=0) 
>>> spamher 
OrderedDict([('h', 2), ('m', 3), ('r', 0), ('s', 6), ('p', 5), ('a', 4), ('e', 1)]) 
>>> 
>>> list(spamher.keys()) 
['h', 'm', 'r', 's', 'p', 'a', 'e'] 
>>> 
>>> spamher = OrderedDict([('s', 6), ('p', 5), ('a', 4), ('m', 3), ('h', 2), ('e', 1), ('r', 0)]) 
>>> list(spamher.keys()) 
['s', 'p', 'a', 'm', 'h', 'e', 'r'] 
>>> 

(Es ist einfach so passiert, dass in Python V3.3.0 Ihre ursprüngliche Beispiel spam die Schlüssel gehalten in ihrer ursprünglichen Reihenfolge von Anfang an ich zu spamher geändert zu bekommen. um diese).

4

Wie die otheranswers erwähnt haben, wird beim Versuch, ein Diktat an OrderedDict zu übergeben oder Schlüsselwortargumente zu verwenden, die Reihenfolge nicht beibehalten. Tupel zu übergeben ist allerdings etwas hässlich, und das ist Python. Es sollte schön sein.

Sie können ab Verwendung __getitem__ auf eine Klasse, um dict-ähnliche Syntax für die Erstellung von OrderedDict "Literale" zu haben: eine OrderedDict

from collections import OrderedDict 
class OD(object): 
    """This class provides a nice way to create OrderedDict "literals".""" 
    def __getitem__(self, slices): 
     if not isinstance(slices, tuple): 
      slices = slices, 
     return OrderedDict((slice.start, slice.stop) for slice in slices) 
# Create a single instance; we don't ever need to refer to the class. 
OD = OD() 

Jetzt können Sie verwenden dict-ähnliche Syntax zu erstellen:

spam = OD['s': (1, 2), 
      'p': (3, 4), 
      'a': (5, 6), 
      'm': (7, 8)] 
assert(''.join(spam.keys()) == 'spam') 

Das funktioniert, weil in den eckigen Klammern, erzeugt Python slice Literale, die wie dict Syntax aussehen passieren, wenn Sie ein wenig schielen.

Die Klasse OD könnte von der Fehlerprüfung profitieren, aber dies zeigt, wie es funktionieren kann.