Warum behalten Python-Sets die Einfügereihenfolge nicht bei?

12

Ich war kürzlich überrascht zu entdecken, dass Diktate zwar die Einfügereihenfolge in Python 3.7+ beibehalten, Sets jedoch nicht:

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['d'] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> s = {'a', 'b', 'c'}
>>> s
{'b', 'a', 'c'}
>>> s.add('d')
>>> s
{'d', 'b', 'a', 'c'}

Was ist der Grund für diesen Unterschied? Gilt die gleiche Effizienzverbesserung, die das Python-Team dazu veranlasst hat, die Diktatimplementierung zu ändern, nicht auch für Sets?

Ich suche nicht nach Hinweisen auf Implementierungen geordneter Mengen oder nach Möglichkeiten, Diktate als Ersatz für Mengen zu verwenden. Ich frage mich nur, warum das Python-Team nicht eingebaute Sets erstellt hat, um die Ordnung zu erhalten, während sie dies für Diktate getan haben.

Bart Robinson
quelle
1
Beantwortet das deine Frage? Hat Python einen geordneten Satz?
Mihai Chelaru
1
Nein, ich verstehe, dass in Python kein geordnetes Set eingebaut ist. Ich frage mich nur, warum das so ist, da jetzt Diktate bestellt werden.
Bart Robinson
4
Die Verwendungsmuster sind unterschiedlich und daher für verschiedene Anwendungsfälle optimiert. Es ist ein weit verbreitetes Missverständnis, dass Mengen in CPython nur Diktate mit Nullwerten sind. Das ist völlig falsch: Die Implementierungen sind unterschiedlich. Wenn Ihre Frage nicht geschlossen wird, kann ich eine detaillierte Antwort posten.
wim
1
"Die Nutzungsmuster sind unterschiedlich und daher für verschiedene Anwendungsfälle optimiert." Eine gute Antwort auf die Frage würde dies näher erläutern, denke ich. Die Frage ist, was die beiden unterschiedlichen Ansätze für die entsprechenden Anwendungsfälle optimal macht.
Karl Knechtel
Beachten Sie, dass PyPy für beide dictund setseit 2.7 dieselbe Reihenfolge verwendet .
MisterMiyagi

Antworten:

10

Sets und Diktate sind für verschiedene Anwendungsfälle optimiert. Die Hauptverwendung eines Sets ist das schnelle Testen der Mitgliedschaft, was auftragsunabhängig ist.Für Diktate sind die Kosten für die Suche die kritischste Operation, und es ist wahrscheinlicher, dass der Schlüssel vorhanden ist. Bei Mengen ist das Vorhandensein oder Fehlen eines Elements nicht im Voraus bekannt, und daher muss die Mengenimplementierung sowohl für den gefundenen als auch für den nicht gefundenen Fall optimiert werden. Einige Optimierungen für allgemeine Mengenoperationen wie Vereinigung und Schnittmenge machen es außerdem schwierig, die Satzreihenfolge beizubehalten, ohne die Leistung zu beeinträchtigen.

Während beide Datenstrukturen auf Hash basieren, ist es ein weit verbreitetes Missverständnis, dass Mengen nur als Dikte mit Nullwerten implementiert werden. Bereits vor der kompakten Diktatimplementierung in CPython 3.6 unterschieden sich die Set- und Diktatimplementierungen erheblich, wobei nur wenig Code wiederverwendet wurde. Zum Beispiel verwenden Diktate eine randomisierte Prüfung, aber Sätze verwenden eine Kombination aus linearer Prüfung und offener Adressierung, um die Cache-Lokalität zu verbessern. Die anfängliche lineare Sonde (standardmäßig 9 Schritte in CPython) überprüft eine Reihe benachbarter Schlüssel / Hash-Paare und verbessert die Leistung, indem die Kosten für die Behandlung von Hash-Kollisionen gesenkt werden. Aufeinanderfolgender Speicherzugriff ist billiger als Streusonden.

  • dictobject.c- master , v3.5.9
  • setobject.c- master , v3.5.9
  • issue18771 - Änderungssatz zur Reduzierung der Kosten für Hash-Kollisionen für festgelegte Objekte in Python 3.4.

Theoretisch wäre es möglich , die Set-Implementierung von CPython so zu ändern, dass sie dem kompakten Diktat ähnelt. In der Praxis gibt es jedoch Nachteile, und namhafte Kernentwickler waren gegen eine solche Änderung.

Sätze bleiben ungeordnet. (Warum? Die Verwendungsmuster sind unterschiedlich. Auch unterschiedliche Implementierung.)

- Guido van Rossum

Sets verwenden einen anderen Algorithmus, der nicht so geändert werden kann, um die Einfügereihenfolge beizubehalten. Set-to-Set-Vorgänge verlieren ihre Flexibilität und Optimierungen, wenn eine Bestellung erforderlich ist. Mengenmathematik wird als ungeordnete Menge definiert. Kurz gesagt, die Bestellung von Sets liegt nicht in unmittelbarer Zukunft.

- Raymond Hettinger

Eine ausführliche Diskussion darüber, ob Sets für 3.7 komprimiert werden sollen, und Antworten darauf, warum dagegen entschieden wurde, finden Sie in den Python-Dev-Mailinglisten.

Zusammenfassend sind die Hauptpunkte, dass die Verwendungsmuster unterschiedlich sind (Einfügungsreihenfolgen wie ** kwargs sind nützlich , weniger für Sets), die Platzersparnis für das Komprimieren von Sets ist weniger bedeutend (da nur Schlüssel- und Hash-Arrays vorhanden sind verdichten (im Gegensatz zu Schlüsseln, Hashes und Werten), und die oben erwähnte lineare Abtastoptimierung in Sätzen ist mit einer kompakten Implementierung nicht kompatibel.

Ich werde den Beitrag von Raymond unten wiedergeben, der die wichtigsten Punkte abdeckt.

Am 14. September 2016 um 15:50 Uhr schrieb Eric Snow:

Dann mache ich dasselbe mit Sets.

Sofern ich nicht falsch verstanden habe, war Raymond dagegen, eine ähnliche Änderung am Set vorzunehmen.

Das stimmt. Hier sind ein paar Gedanken zu diesem Thema, bevor die Leute wild werden.

  • Für das kompakte Diktat war die Platzersparnis ein Nettogewinn, da der zusätzliche Platzbedarf der Indizes und die Gesamtzuordnung für die Schlüssel- / Wert- / Hash-Arrays durch die verbesserte Dichte der Schlüssel- / Wert- / Hash-Arrays mehr als ausgeglichen wurden. Für Sets war das Netz jedoch viel ungünstiger, da wir immer noch die Indizes und die Gesamtzuordnung benötigen, aber die Raumkosten nur durch Verdichtung von nur zwei der drei Arrays ausgleichen können. Mit anderen Worten, das Komprimieren ist sinnvoller, wenn Sie Platz für Schlüssel, Werte und Hashes verschwendet haben. Wenn Sie einen dieser drei verlieren, ist er nicht mehr zwingend.

  • Das Verwendungsmuster für Sets unterscheidet sich von Diktaten. Ersteres hat mehr Hit-or-Miss-Lookups. Letzteres hat tendenziell weniger fehlende Schlüsselsuchen. Einige der Optimierungen für die Set-to-Set-Vorgänge machen es außerdem schwierig, die Set-Reihenfolge beizubehalten, ohne die Leistung zu beeinträchtigen.

  • Ich verfolgte einen alternativen Weg, um die Set-Leistung zu verbessern. Anstatt zu komprimieren (was nicht viel Platz einbrachte und die Kosten für eine zusätzliche Indirektion verursachte), fügte ich eine lineare Prüfung hinzu, um die Kosten für Kollisionen zu senken und die Cache-Leistung zu verbessern. Diese Verbesserung ist nicht kompatibel mit dem von mir für Wörterbücher empfohlenen Komprimierungsansatz.

  • Derzeit ist die Nebenwirkung der Bestellung auf Wörterbücher nicht garantiert, daher ist es verfrüht, darauf zu bestehen, dass auch die Sätze geordnet werden. Die Dokumente enthalten bereits einen Link zu einem Rezept zum Erstellen eines OrderedSet ( https://code.activestate.com/recipes/576694/ ), aber es scheint, dass die Aufnahme nahezu Null war. Nachdem Eric Snow uns ein schnelles OrderedDict zur Verfügung gestellt hat, ist es einfacher als je zuvor, ein OrderedSet aus MutableSet und OrderedDict zu erstellen, aber ich habe auch hier kein wirkliches Interesse festgestellt, da typische Set-to-Set-Datenanalysen dies nicht wirklich tun brauchen oder kümmern sich um die Bestellung. Ebenso ist die primäre Verwendung von schnellen Mitgliedschaftstests auftragsunabhängig.

  • Ich denke jedoch, dass es Raum gibt, PyPI um alternative Set-Implementierungen zu erweitern. Insbesondere gibt es einige interessante Sonderfälle für bestellbare Daten, bei denen Set-to-Set-Vorgänge durch Vergleichen ganzer Tastenbereiche beschleunigt werden können (siehe https://code.activestate.com/recipes/230113-implementation-of- setzt-mit-sortierten-Listen für einen Startpunkt). IIRC, PyPI hat bereits Code für Set-ähnliche Bloom-Filter und Kuckuck-Hashing.

  • Ich verstehe, dass es aufregend ist, einen großen Codeblock in den Python-Kern aufgenommen zu haben, aber das sollte nicht für Schleusen geöffnet werden, um größere Änderungen an anderen Datentypen vorzunehmen, es sei denn, wir sind sicher, dass dies gerechtfertigt ist.

- Raymond Hettinger

Von [Python-Dev] Python 3.6 wird dict kompakt und erhält eine private Version; und Schlüsselwörter werden im September 2016 bestellt.

wim
quelle
2

Diskussionen

Ihre Frage ist deutsch und wurde bereits vor kurzem intensiv über Python-Entwickler diskutiert . R. Hettinger teilte eine Liste von Begründungen in diesem Thread . Der Stand der Ausgabe erscheint jetzt, kurz nach dieser ausführlichen Antwort von T. Peters, offen .

Kurz gesagt, die Implementierung moderner Diktate, die die Einfügereihenfolge beibehalten, ist einzigartig und wird für Mengen nicht als angemessen angesehen. Insbesondere werden Dikte überall verwendet, um Python auszuführen (z. B. __dict__in Namespaces von Objekten). Eine Hauptmotivation für das moderne Diktat war die Reduzierung der Größe, wodurch Python insgesamt speichereffizienter wird. Im Gegensatz dazu sind Mengen in Pythons Kern weniger verbreitet als Diktate und raten daher von einem solchen Refactoring ab. Siehe auch R. Hettingers Vortrag über die Implementierung moderner Diktate.


Perspektiven

Die Unordnung von Mengen in Python entspricht dem Verhalten mathematischer Mengen . Bestellung ist nicht garantiert.

Das entsprechende mathematische Konzept ist ungeordnet und es wäre seltsam, eine solche Ordnung aufzuerlegen - R. Hettinger

Wenn in Python Mengen jeglicher Art eingeführt würden, würde dieses Verhalten einer völlig separaten mathematischen Struktur entsprechen, nämlich einer geordneten Menge (oder Oset). Osets spielen eine separate Rolle in der Mathematik, insbesondere in der Kombinatorik. Eine praktische Anwendung von Osets ist beobachtet Wechsel der Glocken .

Ungeordnete Mengen stehen im Einklang mit einer sehr allgemeinen und allgegenwärtigen Datenstruktur, die die modernste Mathematik, dh die Mengenlehre , aufhebt . Ich reiche ein, ungeordnete Sets in Python sind gut zu haben.

Siehe auch verwandte Beiträge, die dieses Thema erweitern:

Pylang
quelle