it-swarm.com.de

Python OrderedDict-Iteration

Warum wird mein python OrderedDict 'in der falschen Reihenfolge' initialisiert?

Die Lösung hier ist weniger faszinierend als die Erklärung. Es gibt hier etwas, 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
30
neil.millikin

Von den Dokumenten :

Der OrderedDict-Konstruktor und die update() -Methode akzeptieren beide Schlüsselwortargumente, aber ihre Reihenfolge geht verloren, weil Pythons Funktionsaufrufsemantik Schlüsselwortargumente mit einem regulären ungeordneten Wörterbuch weitergibt.

Die Initialisierung verliert also die Reihenfolge, weil sie im Grunde genommen einen Konstruktor mit **kwargs Aufruft.

Edit: In Bezug auf eine Lösung (nicht nur eine Erklärung) - wie gesagt in einem Kommentar des OP , Übergabe einer einzelnen Liste von Tupeln 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

Das liegt daran, dass es nur ein einziges Argument gibt, eine Liste.

37
Chris Krycho

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

Wenn Sie sich die repr () eines OrderedDict ansehen, erhalten Sie einen Hinweis, wie Sie von Anfang an die Reihenfolge festlegen: Sie müssen eine Liste von (Schlüssel-, Wert-) Paaren verwenden, um die Reihenfolge der in der Liste angegebenen Schlüssel beizubehalten.

Hier ist eine, die ich früher gemacht habe:

>>> 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 Ihr ursprüngliches Beispiel für spam die Schlüssel von Anfang an in ihrer ursprünglichen Reihenfolge belassen hat. Ich habe zu spamher gewechselt. um dies herauszufinden).

17
Paddy3118

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

Du kannst abverwenden __getitem__ in einer Klasse, um eine diktähnliche Syntax zum Erstellen von OrderedDict-Literalen zu haben:

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 eine dict-ähnliche Syntax verwenden, um ein OrderedDict zu erstellen:

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

Dies funktioniert, weil in den eckigen Klammern Python= Slice Literale erstellt werden, die beim kleinen Schielen wie diktierte Syntax aussehen.

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

4
Cody Piersall