Warum sind Python-Wörterbücher für Python3.7 nicht umkehrbar?

11

Ab 3.7 behalten Standard-Python-Wörterbücher garantiert die Einfügereihenfolge bei. (*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

Mit anderen Worten, die Diktatschlüssel werden in einer strengen Reihenfolge gehalten. Dies würde im Prinzip ermöglichen, dass die Schlüssel umkehrbar sind. Keine der folgenden Funktionen funktioniert jedoch:

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

Fragen: Was ist der Grund für dieses Verhalten? Warum wurden Diktate nicht umkehrbar gemacht? Gibt es Pläne, dieses Verhalten in Zukunft zu ändern?

Die Problemumgehung funktioniert natürlich:

for k in reversed(list(d.keys())): 
    print(k)

(*) Tatsächlich ist dies bereits bei typischen Installationen von Python 3.6 der Fall, wie in diesem Beitrag erläutert .


Update : Ab Python 3.8 sind Dikte tatsächlich reversibel. Die akzeptierte Antwort bezieht sich auf die Diskussion zwischen Guido und anderen Kernentwicklern, die zu dieser Entscheidung geführt hat. Kurz gesagt, sie gewichteten die Sprachkonsistenz gegen den Implementierungsaufwand und den tatsächlichen Nutzen für die Benutzer.

normanius
quelle

Antworten:

5

Aus den Dokumenten :

umgekehrt ( seq )

Rückgabe rückgängig machen iterator. seq muss ein Objekt sein, das eine __reversed__()Methode hat oder das Sequenzprotokoll unterstützt (die __len__()Methode und die __getitem__()Methode mit ganzzahligen Argumenten ab 0).

Ein dictObjekt wird nicht implementiert __reversed__. Beide letzteren Methoden werden implementiert. Allerdings __getitem__nimmt Schlüssel als Argumente, anstatt ganze Zahlen (ab 0) gewonnen .

Wie, warum ist dies bereits vorgeschlagen und diskutiert hier .

BEARBEITEN:

Diese Zitate stammen aus der Python-Dev-Mailingliste (Thread "Add __reversed__ Methods for dict", gestartet am 25. 05. 18). Ich beginne mit den "konzeptuellen" Argumenten, das erste stammt von Antoine Pitrou:

Es ist nichts wert, dass OrderedDict bereits reverse () unterstützt. Das Argument könnte in beide Richtungen gehen:

  1. dict ähnelt heutzutage OrderedDict, daher sollte es auch reverse () unterstützen.

  2. Sie können OrderedDict verwenden, um explizit zu signalisieren, dass Ihnen die Bestellung wichtig ist. Sie müssen nichts hinzufügen, um zu diktieren.

Mein Gedanke ist, dass die garantierte Einfügereihenfolge für reguläre Diktate brandneu ist, daher wird es eine Weile dauern, bis sich der Begriff festgesetzt hat und Teil des täglichen Denkens über Diktate wird. Sobald dies geschieht, ist es wahrscheinlich unvermeidlich, dass Anwendungsfälle auftreten und __reversed__ irgendwann hinzugefügt wird. Die Implementierung scheint unkompliziert zu sein und es ist kein großer konzeptioneller Sprung zu erwarten, dass eine endlich geordnete Sammlung reversibel ist.

Gefolgt von Raymond Hettingers Antwort:

Angesichts der Tatsache, dass Diktate jetzt die Einfügereihenfolge verfolgen, erscheint es vernünftig, die neuesten Einfügungen kennen zu wollen (dh die zuletzt hinzugefügten Aufgaben in einem Aufgabendiktat zu durchlaufen). Andere mögliche Anwendungsfälle entsprechen wahrscheinlich der Verwendung des Unix-Befehls tail.

Wenn diese Anwendungsfälle auftreten, wäre es schön, wenn __reversed__ bereits unterstützt würde, damit die Benutzer nicht versucht sind, eine hässliche Problemumgehung mithilfe von popitem () -Aufrufen und anschließendem Einfügen zu implementieren.

Das Hauptanliegen in der Mailingliste war, dass dies in zumindest einigen Implementierungen zu viel Aufblähen oder zu einer Verringerung der Speichereffizienz führen würde (doppelt verknüpfte Listen anstelle einfach verknüpfter Listen). Hier ist Inada Naokis Zitat aus Python Bug Tracker ( Ausgabe 33462) ):

"Bestellung haben" bedeutet nicht "umkehrbar". Beispielsweise wird eine einzelne verknüpfte Liste sortiert, aber nicht umkehrbar.

Während die CPython-Implementierung effizient sein kann __reverse__, __reverse__bedeutet das Hinzufügen, dass alle Python-Implementierungen dies bereitstellen. Beispielsweise kann eine Python-Implementierung möglicherweise Diktat mit Hashmap + einzelner verknüpfter Liste implementieren. Wenn __reverse__hinzugefügt wird, ist es nicht mehr möglich.

Zurück zur Mailingliste, hier sind die beiden letzten Nachrichten (beide veröffentlicht am 08.06.2018). Erstens von Michael Selik:

Habe ich Recht, wenn ich sage, dass der Konsens für die Aufnahme in Version 3.8 +1 ist?

Der letzte Punkt im Thread war, dass INADA Naoki verschiedene Implementierungen untersuchte und entschied, dass es in Ordnung ist, diese Funktion in 3.8 aufzunehmen. Soweit ich weiß, stimmte Guido dem Rat von INADA zu, auf die Implementierung von v3.7 durch MicroPython zu warten. Da INADA seine Meinung geändert hat, schätze ich, dass alles dafür ist?

Abschließend die Botschaft von Guido van Rossum:

Das klingt für mich richtig. Wir werden dann zwei Versionen gehabt haben, in denen dies der Fall war:

  • 3.6 wo die Auftragserhaltung in CPython implementiert wurde, jedoch in der Sprachspezifikation

  • 3.7 wo es auch zur Sprachspezifikation hinzugefügt wurde

Wie in den anderen Antworten und Kommentaren erwähnt, reversed()wird es seit Version 3.8 (14.10.2018) sowohl für Diktate als auch für Diktatansichten unterstützt.

gst
quelle
5
Ihr zweites Zitat scheint eine Bias-Auswahl aus diesem Thread zu sein. Der Konsens scheint zu sein, dass die Funktionalität in 3.8 hinzugefügt wird. Auch vor Python 3.7 gab es einfach keine Bestellung für ein normales dictObjekt (zumindest nicht aus der Sprache garantiert), also reversedmachte es auch keinen Sinn
FlyingTeller
Ich habe keinen Hund im Kampf, ich habe nur seine erste Antwort zitiert. Aber Sie haben Recht, Punkt gut genommen - ich habe das Zitat entfernt.
gst
1
Vielen Dank. Der Diskussionsthread des Python-Entwicklers war aufschlussreich. Tatsächlich wurde die Funktion bereits in Python 3.8 implementiert, das erst vor zwei Tagen (14. Oktober 2019) veröffentlicht wurde.
Normanius
Das Dokumentzitat ist nicht hilfreich, es wirft nur die nächste Frage auf: "Warum wird der Diktattyp nicht implementiert __reversed__?" Der Python-Dev-Link hat nützlichen Inhalt, aber die relevanten Teile sollten direkt in der Antwort wiedergegeben werden (da solche externen Links dazu neigen, zu verrotten).
wim
1

Update für Python 3.8

Dict und Dictviews können jetzt in umgekehrter Einfügereihenfolge mit reverse () iteriert werden.

>>> dict = {1: "1", 2: "2", 3: "3"}
>>> reversed(dict)
<dict_reversekeyiterator object at 0x7f72ca795130>
Гончаров Дмитрий
quelle
Trägt diese Antwort etwas Neues bei, das nicht bereits durch andere Antworten, Kommentare oder die Frage selbst abgedeckt ist?
Normanius
@normanius Es hat ein kleines visuelles Codebeispiel, könnte für den schnellen Browser hilfreich sein
jamylak