Sind Wörterbücher in Python 3.6+ bestellt?

Lesezeit: 10 Minuten

Benutzer-Avatar
Chris_Rands

Wörterbücher sind ab Python 3.6 in der Reihenfolge der Einfügung angeordnet. Es wird eher als CPython-Implementierungsdetail als als Sprachfunktion beschrieben. Das Dokumentation Zustände:

dict() verwendet nun eine „kompakte“ Darstellung Pionierarbeit von PyPy. Der Speicherverbrauch des neuen dict() ist zwischen 20 % und 25 % kleiner im Vergleich zu Python 3.5. PEP 468 (Beibehaltung der Reihenfolge von **kwargs in einer Funktion.) wird dadurch implementiert. Der ordnungserhaltende Aspekt dieser neuen Implementierung wird als Implementierungsdetail angesehen und sollte nicht als verlässlich angesehen werden (dies kann sich in Zukunft ändern, aber es ist wünschenswert, diese neue dict-Implementierung für einige Releases in der Sprache zu haben, bevor die Sprachspezifikation geändert wird um eine reihenfolgeerhaltende Semantik für alle aktuellen und zukünftigen Python-Implementierungen vorzuschreiben; dies trägt auch dazu bei, die Abwärtskompatibilität mit älteren Versionen der Sprache zu wahren, in denen noch eine zufällige Iterationsreihenfolge gilt, z. B. Python 3.5). (Beigetragen von INADA Naoki in Ausgabe 27350. Idee ursprünglich von Raymond Hettinger vorgeschlagen.)

Wie funktioniert die neue Wörterbuchimplementierung besser als die ältere, während die Elementreihenfolge beibehalten wird?


Update Dezember 2017: dicts Beibehaltung der Anzeigenreihenfolge ist garantiert für Python 3.7

  • Siehe diesen Thread auf der Python-Dev-Mailingliste: mail.python.org/pipermail/python-dev/2016-September/146327.html falls du es noch nicht gesehen hast; es ist im Grunde eine Diskussion um diese Themen.

    – mgc

    11. Oktober 2016 um 15:11 Uhr

  • Wenn kwargs jetzt bestellt werden sollen (was eine nette Idee ist) und kwargs dict und nicht OrderedDict sind, dann könnte man davon ausgehen, dass dict-Schlüssel in der zukünftigen Version von Python geordnet bleiben, obwohl die Dokumentation etwas anderes sagt.

    – Dmitriy Sintsov

    12. Januar 2017 um 12:32 Uhr

  • @DmitriySintsov Nein, gehe nicht davon aus. Dies war ein Problem, das während des Schreibens des PEP angesprochen wurde, das die Ordnungserhaltungsfunktion von definiert **kwargs und als solche ist die verwendete Formulierung diplomatisch: **kwargs in einer Funktionssignatur ist nun garantiert eine Einfügungsreihenfolge erhaltend Kartierung. Sie haben den Begriff verwendet Kartierung um keine anderen Implementierungen dazu zu zwingen, das bestellte Diktat zu machen (und eine OrderedDict intern) und um zu signalisieren, dass dies nicht davon abhängen soll, dass die dict ist nicht bestellt.

    – Dimitris Fasarakis Hilliard

    4. Februar 2017 um 17:18 Uhr

  • Eine gute Video-Erklärung von Raymond Hettinger

    – Alex

    22. Juli 2017 um 16:38 Uhr

  • @wazoox, die Reihenfolge und Komplexität der Hashmap hat sich nicht geändert. Die Änderung macht die Hashmap kleiner, indem weniger Speicherplatz verschwendet wird, und der eingesparte Speicherplatz ist (normalerweise?) Mehr als das Hilfsarray benötigt. Schneller, kleiner, geordnet – Sie können alle 3 auswählen.

    – John LaRooy

    8. August 2017 um 20:28 Uhr

Benutzer-Avatar
Dimitris Fasarakis Hilliard

Sind Wörterbücher in Python 3.6+ bestellt?

Sie sind Einfügung bestellt[1].

Ab Python 3.6für die CPython-Implementierung von Python, Wörterbücher Merken Sie sich die Reihenfolge der eingefügten Elemente. Dies wird in Python 3.6 als Implementierungsdetail betrachtet; Sie müssen verwenden OrderedDict wenn Sie Insertion Ordering wollen, ist das so garantiert gegenüber anderen Implementierungen von Python (und anderem geordneten Verhalten[1]).

Ab Python 3.7ist dies eine garantierte Sprachfunktion, nicht nur ein Implementierungsdetail. Aus einer Python-Dev-Nachricht von GvR:

Mach es so. „Dict hält Insertionsreihenfolge ein“, lautet das Urteil. Vielen Dank!

Das bedeutet einfach darauf kannst du dich verlassen. Andere Implementierungen von Python müssen ebenfalls ein Insertion Ordered Dictionary anbieten, wenn sie eine konforme Implementierung von Python 3.7 sein wollen.


Wie funktioniert die Python 3.6 Wörterbuchimplementierung performt besser[2] als die ältere unter Beibehaltung der Elementreihenfolge?

Im Wesentlichen durch zwei Arrays halten.

  • Das erste Array, dk_entriesenthält die Einträge (des Typs PyDictKeyEntry) für das Wörterbuch in der Reihenfolge, in der sie eingefügt wurden. Die Beibehaltung der Reihenfolge wird dadurch erreicht, dass es sich um ein Array nur zum Anhängen handelt, bei dem neue Elemente immer am Ende eingefügt werden (Einfügungsreihenfolge).

  • Der Zweite, dk_indicesenthält die Indizes für die dk_entries array (d. h. 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 er zu einem der darin gespeicherten Indizes dk_indices und der entsprechende Eintrag wird durch Indizierung geholt dk_entries. Da nur Indizes gespeichert werden, hängt der Typ dieses Arrays von der Gesamtgröße des Wörterbuchs ab (von type int8_t(1 Byte) zu int32_t/int64_t (4/8 Bytes) an 32/64 Bit baut)

In der vorherigen Implementierung ein Array mit geringer Dichte vom Typ PyDictKeyEntry und Größe dk_size musste zugeteilt werden; Leider führte dies auch zu viel leerem Platz, da dieses Array nicht größer als sein durfte 2/3 * dk_size voll aus Leistungsgründen. (und der leere Raum still hatte PyDictKeyEntry Größe!).

Dies ist jetzt nicht der Fall, da nur die erforderlich Einträge werden gespeichert (diejenigen, die eingefügt wurden) und ein spärliches Array des Typs intX_t (X je nach Diktgröße) 2/3 * dk_sizes voll bleibt. Der Leerraum hat sich vom Typ geändert PyDictKeyEntry zu intX_t.

Also offensichtlich ein spärliches Array von Typen erstellen PyDictKeyEntry ist viel speicherintensiver als ein Sparse-Array zum Speichern ints.

Sie können das vollständige Gespräch sehen auf Python-Dev In Bezug auf diese Funktion ist es bei Interesse eine gute Lektüre.


Im ursprünglichen Vorschlag von Raymond Hettingerist 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 gespeichert als [keyhash, key, value]:

entries = [['--', '--', '--'],
           [-8522787127447073495, 'barry', 'green'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           [-9092791511155847987, 'timmy', 'red'],
           ['--', '--', '--'],
           [-6480567542315338377, 'guido', 'blue']]

Stattdessen sollten die Daten wie folgt organisiert werden:

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 viel Platz im Wesentlichen leer, um Kollisionen zu reduzieren und Suchen schneller zu machen. Mit dem neuen Ansatz reduzieren Sie den erforderlichen Speicher, indem Sie die Sparseness dorthin verschieben, wo sie wirklich benötigt wird, in den Indizes.



[1]: Ich sage “Einfügung bestellt” und nicht “bestellt”, da “bestellt” mit der Existenz von OrderedDict ein weiteres Verhalten nahelegt, das das Objekt “dict” *nicht bereitstellt*. OrderedDicts sind reversibel, bieten reihenfolgeempfindliche Methoden und stellen hauptsächlich reihenfolgeempfindliche Gleichheitstests (`==`, `!=`) bereit. `dict`s bieten derzeit keine dieser Verhaltensweisen/Methoden an.



[2]: Die neuen Wörterbuchimplementierungen funktionieren **speichertechnisch** besser, da sie kompakter gestaltet sind; das ist der Hauptvorteil hier. In Bezug auf die Geschwindigkeit ist der Unterschied nicht so drastisch, es gibt Stellen, an denen das neue Diktat leichte Regressionen einführen könnte (Key-Lookups, zum Beispiel), während in anderen (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.

  • Was passiert also, wenn ein Element entfernt wird? ist der entries Liste verkleinert? oder wird ein Leerzeichen beibehalten? oder wird es von Zeit zu Zeit komprimiert?

    – njzk2

    11. Oktober 2016 um 19:19 Uhr

  • @njzk2 Wenn ein Element entfernt wird, wird der entsprechende Index durch ersetzt DKIX_DUMMY mit einem Wert von -2 und der Eintrag in der entry Reihe ersetzt durch NULLwenn das Einfügen durchgeführt wird, werden die neuen Werte an das Array der Einträge angehängt. Konnte noch nicht erkennen, aber ziemlich sicher, wann sich die Indizes über die hinaus füllen 2/3 Schwellenanpassung wird durchgeführt. Dies kann bei vielen dazu führen, dass sie schrumpfen statt wachsen DUMMY Einträge existieren.

    – Dimitris Fasarakis Hilliard

    11. Oktober 2016 um 20:03 Uhr


  • @Chris_Rands Nein, die einzige tatsächliche Regression, die ich gesehen habe, ist auf dem Tracker in a Nachricht von Viktor. Abgesehen von diesem Mikrobenchmark habe ich kein anderes Problem / keine andere Meldung gesehen, die auf einen ernsthaften Geschwindigkeitsunterschied bei realen Arbeitslasten hinweist. Es gibt Stellen, an denen das neue Diktat leichte Regressionen einführen könnte (z. B. Schlüsselsuchen), während an anderen Stellen (Iteration und Größenänderung) eine Leistungssteigerung vorhanden wäre.

    – Dimitris Fasarakis Hilliard

    14. März 2017 um 13:26 Uhr

  • Korrektur des Teils zur Größenänderung: Wörterbücher ändern ihre Größe nicht, wenn Sie Elemente löschen, sie werden neu berechnet, wenn Sie sie wieder einfügen. Also, wenn ein Diktat mit erstellt wird d = {i:i for i in range(100)} Und Sie .pop alle Artikel ohne Einfügen, die Größe ändert sich nicht. Wenn Sie es erneut hinzufügen, d[1] = 1die passende Größe wird berechnet und die Größe des Diktats angepasst.

    – Dimitris Fasarakis Hilliard

    2. August 2017 um 19:47 Uhr


  • @Chris_Rands Ich bin mir ziemlich sicher, dass es bleibt. Die Sache ist, und der Grund, warum ich meine Antwort geändert habe, um pauschale Aussagen über „dict bestellt werden’, dicts sind in dem Sinne nicht geordnet OrderedDicts sind. Das bemerkenswerteste Thema ist die Gleichberechtigung. dicts sind reihenfolgeunempfindlich ==, OrderedDicts haben reihenfolgeempfindliche. Schluss machen OrderedDicts und ändern dicts Jetzt auftragsabhängige Vergleiche zu haben, könnte zu vielen Brüchen in altem Code führen. Ich vermute, das einzige, was sich ändern könnte OrderedDicts ist seine Umsetzung.

    – Dimitris Fasarakis Hilliard

    10. April 2018 um 16:57 Uhr


Benutzer-Avatar
Maresh

Nachfolgend wird die ursprüngliche erste Frage beantwortet:

Sollte ich es benutzen dict oder OrderedDict in Python 3.6?

Ich denke, dieser Satz aus der Dokumentation reicht eigentlich aus, um Ihre Frage zu beantworten

Der ordnungserhaltende Aspekt dieser neuen Implementierung wird als Implementierungsdetail betrachtet und sollte nicht als verlässlich angesehen werden

dict ist nicht ausdrücklich als geordnete Sammlung gedacht, wenn Sie also konsistent bleiben und sich nicht auf einen Nebeneffekt der neuen Implementierung verlassen möchten, sollten Sie dabei bleiben OrderedDict.

Machen Sie Ihren Code zukunftssicher 🙂

Darüber gibt es eine Debatte hier.

BEARBEITEN: Python 3.7 behält dies als Feature bei sehen

Benutzer-Avatar
fjsj

Update: Guido van Rossum auf der Mailingliste angekündigt das ab Python 3.7 dicts in allen Python-Implementierungen müssen die Reihenfolge der Einfügung beibehalten.

  • Nun, da die Schlüsselreihenfolge der offizielle Standard ist, was ist der Zweck des OrderedDict? Oder ist es jetzt überflüssig?

    – Jonny Waffeln

    14. Juni 2018 um 18:54 Uhr

  • Ich denke, OrderedDict wird nicht überflüssig sein, weil es das hat move_to_end Methode und ihre Gleichheit ist ordnungsabhängig: docs.python.org/3/library/…. Siehe die Anmerkung zu Jim Fasarakis Hilliards Antwort.

    – fjsj

    15. Juni 2018 um 20:05 Uhr


  • @JonnyWaffles siehe Jims Antwort und diese Fragen und Antworten stackoverflow.com/questions/50872498/…

    – Chris_Rands

    25. Juni 2018 um 21:15 Uhr

  • Wenn Sie möchten, dass Ihr Code auf 2.7 und 3.6/3.7+ gleich ausgeführt wird, müssen Sie OrderedDict verwenden

    – Bootscoder

    28. Juni 2018 um 17:51 Uhr

  • Wahrscheinlich wird es bald ein “UnorderedDict” für Leute geben, die ihre Diktate aus Sicherheitsgründen ärgern möchten ;p

    – ZF007

    3. Juni 2019 um 0:18 Uhr

Benutzer-Avatar
Rkengler

Ich wollte zur obigen Diskussion beitragen, habe aber nicht den Ruf, einen Kommentar abzugeben.

Python 3.8 enthält die reversed() Funktion in Wörterbüchern (Entfernen eines weiteren Unterschieds aus OrderedDict.

Dict und dictviews können jetzt mit reversed() in umgekehrter Einfügereihenfolge iteriert werden. (Beigetragen von Rémi Lapeyre in bpo-33462.)
Sehen Sie, was es Neues in Python 3.8 gibt

Ich sehe keine Erwähnung des Gleichheitsoperators oder anderer Funktionen von OrderedDict sie sind also immer noch nicht ganz gleich.

Benutzer-Avatar
Peng

Um diese Frage im Jahr 2020 vollständig zu beantworten, lassen Sie mich einige Aussagen zitieren offizielle Python-Dokumentation:

Geändert in Version 3.7: Die Wörterbuchreihenfolge ist garantiert die Reihenfolge der Einfügungen. Dieses Verhalten war ein Implementierungsdetail von CPython von 3.6.

Geändert in Version 3.7: Die Wörterbuchreihenfolge ist garantiert die Reihenfolge der Einfügungen.

Geändert in Version 3.8: Wörterbücher sind jetzt umkehrbar.

Wörterbücher und Wörterbuchansichten sind reversibel.

EIN Aussage bezüglich OrderedDict vs. Dict:

Geordnete Wörterbücher sind genau wie normale Wörterbücher, haben aber einige zusätzliche Funktionen in Bezug auf Bestellvorgänge. Sie haben an Bedeutung verloren, seit die eingebaute Klasse dict die Fähigkeit erlangt hat, sich die Reihenfolge der Einfügungen zu merken (dieses neue Verhalten wurde in Python 3.7 garantiert).

1130310cookie-checkSind Wörterbücher in Python 3.6+ bestellt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy