Wie halte ich Schlüssel / Werte in der angegebenen Reihenfolge?

319

Ich habe ein Wörterbuch, das ich in einer bestimmten Reihenfolge deklariert habe, und möchte es die ganze Zeit in dieser Reihenfolge behalten. Die Schlüssel / Werte können nicht wirklich in der Reihenfolge ihres Werts gehalten werden. Ich möchte sie nur in der Reihenfolge, in der ich sie deklariert habe.

Also, wenn ich das Wörterbuch habe:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

Es ist nicht in dieser Reihenfolge, wenn ich es ansehe oder durchlaufe. Gibt es eine Möglichkeit, um sicherzustellen, dass Python die explizite Reihenfolge beibehält, in der ich die Schlüssel / Werte deklariert habe?

Roflwaffel
quelle

Antworten:

229

Ab Python 3.6 dictbehält der Standardtyp standardmäßig die Einfügereihenfolge bei.

Definieren

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

führt zu einem Wörterbuch mit den Schlüsseln in der im Quellcode angegebenen Reihenfolge.

Dies wurde erreicht, indem ein einfaches Array mit Ganzzahlen für die Sparse-Hash-Tabelle verwendet wurde, wobei diese Ganzzahlen in ein anderes Array indizieren, in dem die Schlüssel-Wert-Paare (plus den berechneten Hash) gespeichert sind. Dieses letztere Array speichert die Elemente zufällig in der Einfügereihenfolge, und die gesamte Kombination benötigt tatsächlich weniger Speicher als die in Python 3.5 und früher verwendete Implementierung. Weitere Informationen finden Sie im ursprünglichen Ideenbeitrag von Raymond Hettinger .

In 3.6 wurde dies immer noch als Implementierungsdetail betrachtet. finden Sie in dem Was ist neu in Python 3.6 Dokumentation :

Der auftragserhaltende Aspekt dieser neuen Implementierung wird als Implementierungsdetail betrachtet und sollte nicht als verlässlich angesehen werden (dies kann sich in Zukunft ändern, es ist jedoch erwünscht, diese neue Dikt-Implementierung für einige Releases in der Sprache zu haben, bevor die Sprachspezifikation geändert wird Dies trägt auch dazu bei, die Abwärtskompatibilität mit älteren Versionen der Sprache zu gewährleisten, in denen die zufällige Iterationsreihenfolge noch gültig ist (z. B. Python 3.5).

Python 3.7 erweitert dieses Implementierungsdetail auf eine Sprachspezifikation. Daher ist es jetzt obligatorisch, dictdie Reihenfolge in allen Python-Implementierungen beizubehalten, die mit dieser oder einer neueren Version kompatibel sind. Siehe die Erklärung der BDFL .

In bestimmten Fällen möchten Sie die collections.OrderedDict()Klasse möglicherweise weiterhin verwenden , da sie zusätzlich zum Standardtyp einige zusätzliche Funktionen bietet dict. B. umkehrbar zu sein (dies erstreckt sich auf die Ansichtsobjekte ) und die Neuordnung zu unterstützen (über die move_to_end()Methode ).

Martijn Pieters
quelle
Leider ist dies für Wörterbuch nicht relevant Schöpfung . Es wird nicht garantiert, dass Wörterbücher, die wie im Beispiel im Code angegeben sind, dieselbe Reihenfolge beibehalten. Dies bedeutet, dass Sie ein leeres Diktat erstellen und jedes Element manuell einfügen müssen, wenn Sie die Reihenfolge steuern möchten.
naught101
8
@ naught101: nein, dies gilt für die Erstellung von Diktaten . In Python 3.7 und höher wird bei Verwendung einer Diktatanzeige, wie in der Frage gezeigt, garantiert, dass diese Schlüssel der Reihe nach aufgelistet werden . Die geschriebenen Schlüssel-Wert-Paare werden von links nach rechts eingefügt, da die Sprachspezifikation Folgendes garantiert : Wenn eine durch Kommas getrennte Folge von Schlüssel / Bezugspaaren angegeben wird, werden sie von links nach rechts ausgewertet, um die Einträge des Wörterbuchs zu definieren . Die dict()Dokumentation enthält sogar ein Beispiel.
Martijn Pieters
175
from collections import OrderedDict
OrderedDict((word, True) for word in words)

enthält

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

Wenn die Werte True(oder ein anderes unveränderliches Objekt) sind, können Sie auch Folgendes verwenden:

OrderedDict.fromkeys(words, True)
Eumiro
quelle
2
Bemerkenswert ist natürlich, dass der "unveränderliche" Teil keine feste Regel ist, die Python durchsetzen wird - es ist "nur" eine gute Idee.
lvc
11
OrderedDict(FUTURE=[], TODAY=[], PAST=[])Beachten Sie, dass Lösungen wie: nicht funktionieren, wenn Ansatz erwähnt: OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])Ordnung halten.
Andilabs
2
@andi Ich habe ein anderes Problem, wenn ich jsonify verwende, scheint das OrderedDict seine Reihenfolge verloren zu haben, wenn die json-Daten generiert werden. Wie auch immer, um dies zu lösen?
Tyan
github.com/pallets/flask/issues/974 Dies kann verwendet werden, um das Problem zu lösen ..
Tyan
5
In Python3.7 ist das Diktat jetzt standardmäßig angeordnet. mail.python.org/pipermail/python-dev/2017-December/151283.html
sertsedat
167

Anstatt den theoretischen Teil zu erklären, werde ich ein einfaches Beispiel geben.

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}
Mohit Dabas
quelle
16
Gibt es eine Möglichkeit, OrderedDict wie den Dict-Typ massenweise zuzuweisen?
Tyan
2
OrderedDictlöst zwar das Problem, aber ... in diesem speziellen Beispiel erhalten Sie genau das gleiche Ergebnis mit einem Standardwörterbuch
Tonechas
2
@Tonechas: Ich habe das Beispiel gerade mit einem Standardwörterbuch ausprobiert und bekam {'aol': 1, 'foo': 3}Also denke ich, dass es ein gutes anschauliches Beispiel ist.
Twasbrillig
4
Es gibt für jeden eine Lektion: Es wurde festgestellt (ich denke um die Version 2.4), dass Pythons vorhersehbares Hashing zu Sicherheitslücken führen kann. Daher gibt es jetzt keine Garantie dafür, dass selbst zwei verschiedene Läufe desselben Codes in einem Standard dieselbe Reihenfolge aufweisen diktieren.
Holdenweb
1
@tyan können Sie OrderedDict.update()mit einer Iterable aufrufen, die Schlüssel-Wert-Paare enthält : d1.upate([(key1, val1), (key2, val2)]).
Ruud Althuizen
36

Beachten Sie, dass diese Antwort für Python-Versionen vor Python3.7 gilt. CPython 3.6 behält unter den meisten Umständen die Einfügereihenfolge als Implementierungsdetail bei. Ab Python3.7 wurde erklärt, dass Implementierungen die Einfügereihenfolge beibehalten MÜSSEN, um kompatibel zu sein.


Python-Wörterbücher sind ungeordnet. Wenn Sie ein geordnetes Wörterbuch möchten, versuchen Sie es mit collection.OrderedDict .

Beachten Sie, dass OrderedDict in Python 2.7 in die Standardbibliothek eingeführt wurde. Wenn Sie eine ältere Version von Python haben, finden Sie auf ActiveState Rezepte für bestellte Wörterbücher .

mgilson
quelle
siehe @ martijns Beitrag oben. Ab Python 3.6 unterstützt dict die Einfügereihenfolge.
tpk
12

Wörterbücher verwenden eine Reihenfolge, die die Suche effizient macht, und das können Sie nicht ändern.

Sie können einfach eine Liste von Objekten verwenden (in einem einfachen Fall ein 2-Element-Tupel oder sogar eine Klasse) und Elemente an das Ende anhängen. Sie können dann die lineare Suche verwenden, um darin enthaltene Elemente zu finden.

Alternativ können Sie eine andere Datenstruktur erstellen oder verwenden, die mit der Absicht erstellt wurde, die Reihenfolge aufrechtzuerhalten.

Fire Lancer
quelle
Wörterbücher verwenden eine Reihenfolge, die die Suche effizient macht. Schließlich hat jemand darauf hingewiesen.
scharette
7

Ich bin auf diesen Beitrag gestoßen, als ich versucht habe herauszufinden, wie OrderedDict funktioniert. PyDev für Eclipse konnte OrderedDict überhaupt nicht finden, und so entschied ich mich, ein Tupel der Schlüsselwerte meines Wörterbuchs zu erstellen, da ich möchte, dass sie bestellt werden. Als ich meine Liste ausgeben musste, habe ich nur die Werte des Tupels durchlaufen und den iterierten 'Schlüssel' aus dem Tupel in das Wörterbuch eingefügt, um meine Werte in der Reihenfolge abzurufen, in der ich sie benötigte.

Beispiel:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

Es ist ein bisschen umständlich, aber ich bin unter Zeitdruck und es ist die Problemumgehung, die ich mir ausgedacht habe.

Hinweis: Der Listenlistenansatz, den jemand anderes vorgeschlagen hat, ist für mich nicht wirklich sinnvoll, da Listen geordnet und indiziert sind (und auch eine andere Struktur als Wörterbücher haben).

smushface
quelle
Tolle Lösung. Ich werde es verwenden, um json in eine Datei zu schreiben, immer in der gleichen Reihenfolge.
Hrvoje T
6

Mit einem Wörterbuch können Sie nicht wirklich das tun, was Sie wollen. Sie haben das Wörterbuch bereits d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}erstellt. Ich habe festgestellt, dass es keine Möglichkeit gibt, in Ordnung zu bleiben, wenn es bereits erstellt wurde. Ich habe stattdessen eine JSON-Datei mit dem Objekt erstellt:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

Ich benutzte:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

dann verwendet:

print json.dumps(r)

verifizieren.

eifersüchtig3
quelle
1
Warum also nicht mit einem OrderedDict aus einer Liste beginnen? Die JSON-Datei fügt hier eigentlich nichts hinzu.
Martijn Pieters
Ja, Liste ist nützlicher, um Ordnung zu halten, aber die Antwort bezog sich auf die Frage nach der Bestellung von Wörterbüchern. Informieren Sie die Benutzer lediglich über die Einschränkungen bei der Verwendung eines Wörterbuchs und geben Sie ihnen eine mögliche Lösung, wenn sie aus irgendeinem Grund ein Wörterbuch verwenden müssen.
eifersüchtig3
1
Aber dieser Teil wird bereits von viel älteren Antworten aus dem Jahr 2012 abgedeckt.
Martijn Pieters
3
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])
user11502624
quelle
Hauptproblem, das kein Diktat mehr ist, es ist eine Liste von Tupeln
Oleg
2

Eine andere Alternative ist die Verwendung von Pandas, dataframeda dies die Reihenfolge und die Indexpositionen der Elemente in einer diktartigen Struktur garantiert.

Ville
quelle
1

Im Allgemeinen können Sie eine Klasse entwerfen , dass verhält sich wie ein Wörterbuch, in erster Linie sein , die Methoden der Umsetzung __contains__, __getitem__, __delitem__, __setitem__und einige mehr. Diese Klasse kann ein beliebiges Verhalten haben, z. B. indem Sie einen sortierten Iterator über die Schlüssel verteilen ...

Rasmus Kaj
quelle
1

Wenn Sie ein Wörterbuch in einer bestimmten Reihenfolge haben möchten, können Sie auch eine Liste von Listen erstellen, in der das erste Element der Schlüssel und das zweite Element der Wert ist und wie in diesem Beispiel aussieht

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3
Pelos
quelle
7
Dies ist kein "Wörterbuch", da Sie Elemente nicht anhand ihres Schlüssels suchen können, ohne die gesamte Sammlung zu durchsuchen (wobei O (n) Zeit benötigt wird).
BHSPitMonkey
1
Ja, es ist kein Wörterbuch, aber je nach Situation könnte es eine gültige Lösung für das Problem des Originalplakats bieten.
SunSparc
er hat nicht genau gesagt, wie wir wollten, nur um sie bestellen zu können =), wie immer gibt es viele Möglichkeiten, eine Sache zu tun.
pelos
1

Ich hatte ein ähnliches Problem bei der Entwicklung eines Django-Projekts. Ich konnte OrderedDict nicht verwenden, da ich eine alte Version von Python ausführte. Daher bestand die Lösung darin, die SortedDict-Klasse von Django zu verwenden:

https://code.djangoproject.com/wiki/SortedDict

z.B,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

Hinweis: Diese Antwort stammt ursprünglich aus dem Jahr 2011. Wenn Sie Zugriff auf Python Version 2.7 oder höher haben, sollten Sie Zugriff auf den Now-Standard haben collections.OrderedDict, von dem viele Beispiele von anderen in diesem Thread bereitgestellt wurden.

Evan B.
quelle
0

Sie können das gleiche tun, was ich für das Wörterbuch getan habe.

Erstellen Sie eine Liste und ein leeres Wörterbuch:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', '[email protected]']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
Himanshu Kanojiya
quelle