Sind Wörterbücher in Python 3.6+ bestellt?
Sie sind Einfügungsreihenfolge [1] . Ab Python 3.6 merken sich Wörterbücher für die CPython-Implementierung von Python die Reihenfolge der eingefügten Elemente . Dies wird in Python 3.6 als Implementierungsdetail betrachtet . Sie müssen verwenden, OrderedDict
wenn Sie eine Einfügereihenfolge wünschen, die für andere Implementierungen von Python (und anderes geordnetes Verhalten [1] ) garantiert ist .
Ab Python 3.7 ist dies kein Implementierungsdetail mehr, sondern wird zu einer Sprachfunktion. Aus einer Python-Dev-Nachricht von GvR :
Mach es so. "Dikt hält Einfügereihenfolge" ist das Urteil. Vielen Dank!
Dies bedeutet einfach, dass Sie sich darauf verlassen können . Andere Implementierungen von Python müssen ebenfalls ein Wörterbuch mit Einfügungsreihenfolge anbieten, wenn sie eine konforme Implementierung von Python 3.7 sein sollen.
Wie funktioniert die Python- 3.6
Wörterbuchimplementierung besser [2] als die ältere, während die Elementreihenfolge beibehalten wird?
Im Wesentlichen durch Beibehalten von zwei Arrays .
Das erste Array dk_entries
enthält die Einträge ( vom TypPyDictKeyEntry
) für das Wörterbuch in der Reihenfolge, in der sie eingefügt wurden. Die Beibehaltung der Reihenfolge wird dadurch erreicht, dass dies ein Array nur zum Anhängen ist, in das immer neue Elemente am Ende eingefügt werden (Einfügereihenfolge).
Die zweite dk_indices
enthält die Indizes für das dk_entries
Array (dh Werte, die die Position des entsprechenden Eintrags in angeben dk_entries
). Dieses Array fungiert als Hash-Tabelle. Wenn ein Schlüssel gehasht wird, führt dies zu einem der darin gespeicherten Indizes, dk_indices
und der entsprechende Eintrag wird durch Indizierung abgerufen dk_entries
. Da nur Indizes beibehalten werden, hängt der Typ dieses Arrays von der Gesamtgröße des Wörterbuchs ab (von Typ int8_t
( 1
Byte) bis int32_t
/ int64_t
( 4
/ 8
Byte) bei 32
/ 64
Bit-Builds).
In der vorherigen Implementierung musste ein spärliches Array von Typ PyDictKeyEntry
und Größe dk_size
zugewiesen werden. Leider führte dies auch zu viel leerem Speicherplatz, da dieses Array aus Leistungsgründen nicht mehr als 2/3 * dk_size
voll sein durfte . (und der leere Raum noch hatte eine PyDictKeyEntry
Größe!).
Dies ist jetzt nicht der Fall, da nur die erforderlichen Einträge gespeichert werden (die eingefügt wurden) und ein spärliches Array vom Typ intX_t
( X
abhängig von der Größe des Diktats) 2/3 * dk_size
voll bleibt. Der leere Raum wurde von Typ PyDictKeyEntry
zu geändert intX_t
.
Das Erstellen eines spärlichen Arrays vom Typ PyDictKeyEntry
ist daher viel speicherintensiver als ein spärliches Array zum Speichern von int
s.
Sie können die vollständige Konversation auf Python-Dev über diese Funktion sehen, wenn Sie interessiert sind, es ist eine gute Lektüre.
In dem ursprünglichen Vorschlag von Raymond Hettinger ist eine Visualisierung der verwendeten Datenstrukturen zu sehen, die den Kern der Idee erfasst.
Zum Beispiel das Wörterbuch:
d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}
wird derzeit als [keyhash, key, value] gespeichert:
entries = [['--', '--', '--'],
[-8522787127447073495, 'barry', 'green'],
['--', '--', '--'],
['--', '--', '--'],
['--', '--', '--'],
[-9092791511155847987, 'timmy', 'red'],
['--', '--', '--'],
[-6480567542315338377, 'guido', 'blue']]
Stattdessen sollten die Daten wie folgt organisiert sein:
indices = [None, 1, None, None, None, 0, None, 2]
entries = [[-9092791511155847987, 'timmy', 'red'],
[-8522787127447073495, 'barry', 'green'],
[-6480567542315338377, 'guido', 'blue']]
Wie Sie jetzt visuell sehen können, ist im ursprünglichen Vorschlag im Wesentlichen viel Platz leer, um Kollisionen zu reduzieren und das Nachschlagen zu beschleunigen. Mit dem neuen Ansatz reduzieren Sie den erforderlichen Speicher, indem Sie die Spärlichkeit in den Indizes dorthin verschieben, wo sie wirklich benötigt wird.
[1]: Ich sage "Einfügung bestellt" und nicht "bestellt", da "bestellt" mit der Existenz von OrderedDict weiteres Verhalten nahe legt, das das dict
Objekt nicht bereitstellt . OrderedDicts sind reversibel, bieten auftragssensitive Methoden und bieten hauptsächlich auftragssensitive Gleichheitstests ( ==
, !=
). dict
s bieten derzeit keine dieser Verhaltensweisen / Methoden an.
[2]: Die neuen Wörterbuchimplementierungen bieten eine bessere Speicherleistung, da sie kompakter gestaltet sind. Das ist hier der Hauptvorteil. In Bezug auf die Geschwindigkeit ist der Unterschied nicht so drastisch. Es gibt Stellen, an denen das neue Diktat leichte Regressionen einführen kann ( z. B. Key-Lookups ), während in anderen Fällen (Iteration und Größenänderung) ein Leistungsschub vorhanden sein sollte.
Insgesamt verbessert sich die Leistung des Wörterbuchs, insbesondere in realen Situationen, aufgrund der eingeführten Kompaktheit.