Was sind Wörterbuchansichtsobjekte?

158

In Python 2.7 sind die Methoden für die Wörterbuchansicht verfügbar.

Jetzt kenne ich die Vor- und Nachteile der folgenden Punkte:

  • dict.items()(und values, keys): Gibt eine Liste zurück, damit Sie das Ergebnis tatsächlich speichern können, und
  • dict.iteritems() (und dergleichen): Gibt einen Generator zurück, sodass Sie jeden generierten Wert einzeln durchlaufen können.

Wofür dict.viewitems()(und dergleichen)? Was sind ihre Vorteile? Wie funktioniert es? Was ist denn eine Aussicht?

Ich habe gelesen, dass die Ansicht immer die Änderungen aus dem Wörterbuch widerspiegelt. Aber wie verhält es sich aus Sicht der Perfektion und des Gedächtnisses? Was sind die Vor- und Nachteile?

e-satis
quelle

Antworten:

157

Wörterbuchansichten sind im Wesentlichen das, was ihr Name sagt: Ansichten sind einfach wie ein Fenster auf den Schlüsseln und Werten (oder Elementen) eines Wörterbuchs. Hier ist ein Auszug aus der offiziellen Dokumentation zu Python 3:

>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
>>> keys = dishes.keys()
>>> values = dishes.values()

>>> # view objects are dynamic and reflect dict changes
>>> del dishes['eggs']
>>> keys  # No eggs anymore!
dict_keys(['sausage', 'bacon', 'spam'])

>>> values  # No eggs value (2) anymore!
dict_values([1, 1, 500])

(Das Python 2-Äquivalent verwendet dishes.viewkeys()und dishes.viewvalues().)

Dieses Beispiel zeigt den dynamischen Charakter von Ansichten : Die Schlüsselansicht ist keine Kopie der Schlüssel zu einem bestimmten Zeitpunkt, sondern ein einfaches Fenster, in dem die Schlüssel angezeigt werden. Wenn sie geändert werden, ändert sich auch das, was Sie durch das Fenster sehen. Diese Funktion kann unter bestimmten Umständen nützlich sein (z. B. kann man mit einer Ansicht der Schlüssel in mehreren Teilen eines Programms arbeiten, anstatt die aktuelle Liste der Schlüssel jedes Mal neu zu berechnen, wenn sie benötigt werden) - beachten Sie, dass die Wörterbuchschlüssel geändert werden Während der Iteration über die Ansicht ist nicht genau definiert, wie sich der Iterator verhalten soll, was zu Fehlern führen kann .

Ein Vorteil besteht darin, dass beispielsweise beim Betrachten der Schlüssel nur eine kleine und feste Menge an Speicher benötigt wird und eine kleine und feste Menge an Prozessorzeit erforderlich ist , da keine Liste von Schlüsseln erstellt wird (Python 2 hingegen). Oft wird unnötigerweise eine neue Liste erstellt, wie von Rajendran T zitiert, die Speicher und Zeit in einer Menge benötigt, die proportional zur Länge der Liste ist. Um die Fensteranalogie fortzusetzen, wenn Sie eine Landschaft hinter einer Wand sehen möchten, machen Sie einfach eine Öffnung darin (Sie bauen ein Fenster); Das Kopieren der Schlüssel in eine Liste würde dem Malen einer Kopie der Landschaft an Ihre Wand entsprechen. Die Kopie benötigt Zeit, Platz und wird nicht selbst aktualisiert.

Zusammenfassend sind Ansichten einfach… Ansichten (Fenster) in Ihrem Wörterbuch, die den Inhalt des Wörterbuchs auch nach dessen Änderung anzeigen. Sie bieten Funktionen, die sich von denen von Listen unterscheiden: Eine Liste von Schlüsseln enthält eine Kopie der Wörterbuchschlüssel zu einem bestimmten Zeitpunkt, während eine Ansicht dynamisch ist und viel schneller abgerufen werden kann, da keine Daten kopiert werden müssen ( Schlüssel oder Werte), um erstellt zu werden.

Eric O Lebigot
quelle
6
+1. Ok, wie unterscheidet sich das vom direkten Zugriff auf die interne Schlüsselliste? Ist das schneller, langsamer? Speichereffizienter? Eingeschränkt? Wenn Sie es lesen und bearbeiten können, fühlt es sich genauso an, als hätte man einen Verweis auf diese Liste.
E-
3
Vielen Dank. Die Sache ist die, dass die Ansichten sind Ihr Zugriff auf „der interne Liste der Tasten“ ( man beachte , dass diese „Liste des Schlüssels“ ist kein Python - Liste, obwohl, sondern gerade eine Ansicht). Ansichten sind speichereffizienter als die Listen der Schlüssel (oder Werte oder Elemente) von Python 2, da sie nichts kopieren. Sie sind in der Tat wie "ein Verweis auf die Liste der Schlüssel" (beachten Sie auch, dass "ein Verweis auf eine Liste" in Python einfach als Liste bezeichnet wird, da Listen veränderbare Objekte sind). Beachten Sie auch, dass Sie Ansichten nicht direkt bearbeiten können. Stattdessen bearbeiten Sie das Wörterbuch weiterhin und die Ansichten spiegeln Ihre Änderungen sofort wider.
Eric O Lebigot
3
Ok, ich bin mir noch nicht sicher über die Implementierung, aber es ist die beste Antwort bisher.
E-Satis
2
Vielen Dank. In der Tat geht es bei dieser Antwort hauptsächlich um die Semantik von Ansichten. Ich habe keine Informationen über ihre Implementierung in CPython, aber ich würde vermuten, dass eine Ansicht im Grunde ein Zeiger auf die richtige (n) Struktur (en) (Schlüssel und / oder Werte) ist und dass die Strukturen Teil des Wörterbuchobjekts selbst sind.
Eric O Lebigot
5
Ich denke, es ist erwähnenswert, dass der Beispielcode in diesem Beitrag von Python3 stammt und nicht das ist, was ich in Python2.7 bekomme.
15.
21

Wie Sie bereits erwähnt haben, wird dict.items()eine Kopie der Liste der (Schlüssel-, Wert-) Paare des Wörterbuchs zurückgegeben, was verschwenderisch ist und dict.iteritems()einen Iterator über die (Schlüssel-, Wert-) Paare des Wörterbuchs zurückgibt.

Nehmen Sie nun das folgende Beispiel, um den Unterschied zwischen einem Diktatorinterprator und einer Diktatansicht zu sehen

>>> d = {"x":5, "y":3}
>>> iter = d.iteritems()
>>> del d["x"]
>>> for i in iter: print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Während eine Ansicht Ihnen einfach zeigt, was im Diktat steht. Es ist egal, ob es sich geändert hat:

>>> d = {"x":5, "y":3}
>>> v = d.viewitems()
>>> v
dict_items([('y', 3), ('x', 5)])
>>> del d["x"]
>>> v
dict_items([('y', 3)])

Eine Ansicht ist einfach so, wie das Wörterbuch jetzt aussieht. Nach dem Löschen wäre ein Eintrag .items()veraltet gewesen und .iteritems()hätte einen Fehler ausgelöst.

Martin Konecny
quelle
Tolles Beispiel, danke. Allerdings sollte v = d.items () nicht v - d.viewitems ()
rix
1
Die Frage bezieht sich auf Python 2.7, viewitems()ist also tatsächlich richtig ( items()gibt eine Ansicht in Python 3 korrekt wieder ).
Eric O Lebigot
Eine Ansicht kann jedoch nicht verwendet werden, um ein Wörterbuch zu durchlaufen, während es geändert wird.
Ioannis Filippidis
18

Schon beim Lesen der Dokumente habe ich den Eindruck:

  1. Ansichten sind "pseudo-set-artig", da sie die Indizierung nicht unterstützen. Sie können sie also auf Mitgliedschaft testen und über sie iterieren (da Schlüssel hashbar und eindeutig sind, sind die Ansichten für Schlüssel und Elemente mehr ". set-like ", da sie keine Duplikate enthalten).
  2. Sie können sie speichern und mehrfach verwenden, wie die Listenversionen.
  3. Da sie das zugrunde liegende Wörterbuch widerspiegeln, ändert jede Änderung im Wörterbuch die Ansicht und mit ziemlicher Sicherheit die Reihenfolge der Iteration . Im Gegensatz zu den Listenversionen sind sie also nicht "stabil".
  4. Da sie das zugrunde liegende Wörterbuch widerspiegeln, handelt es sich mit ziemlicher Sicherheit um kleine Proxy-Objekte. Das Kopieren der Schlüssel / Werte / Elemente würde erfordern, dass sie das Originalwörterbuch irgendwie ansehen und es mehrmals kopieren, wenn Änderungen auftreten, was eine absurde Implementierung wäre. Ich würde also sehr wenig Speicheraufwand erwarten, aber der Zugriff ist etwas langsamer als direkt auf das Wörterbuch.

Ich denke, der Schlüsselverwendungsfall ist, wenn Sie ein Wörterbuch behalten und wiederholt über seine Schlüssel / Elemente / Werte iterieren, wobei dazwischen Änderungen vorgenommen werden. Sie könnten nur einen Blick stattdessen verwenden, Drehen for k, v in mydict.iteritems():in for k, v in myview:. Aber wenn Sie das Wörterbuch nur einmal durchlaufen, sind die Iterationsversionen meiner Meinung nach immer noch vorzuziehen.

Ben
quelle
2
+1 für die Analyse der Vor- und Nachteile der wenigen Informationen, die wir erhalten haben.
E-
Wenn ich einen Iterator über einer Ansicht erstelle, wird dieser immer noch ungültig, wenn sich das Wörterbuch ändert. Das ist das gleiche Problem wie bei einem Iterator über das Wörterbuch selbst (z iteritems(). B. ). Was ist der Sinn dieser Ansichten? Wann bin ich froh, sie zu haben?
Alfe
@Alfe Du hast Recht, das ist ein Problem mit der Wörterbuchiteration und Ansichten helfen überhaupt nicht dabei. Angenommen, Sie müssen die Werte eines Wörterbuchs an eine Funktion übergeben. Sie könnten verwenden .values(), aber das beinhaltet das Erstellen einer ganzen Kopie als Liste, was teuer sein könnte. Es gibt, .itervalues()aber Sie können diese nicht mehr als einmal konsumieren, so dass es nicht mit jeder Funktion funktioniert. Ansichten erfordern keine teure Kopie, sind jedoch als eigenständiger Wert nützlicher als ein Iterator. Aber sie sollen immer noch nicht beim gleichzeitigen Iterieren und Ändern helfen (dort möchten Sie wirklich eine Kopie).
Ben
17

Die Ansicht Methoden geben eine Liste (nicht eine Kopie der Liste, im Vergleich zu .keys(), .items()und .values()), so dass es leichter ist, sondern spiegelt den aktuellen Inhalt des Wörterbuchs.

Ab Python 3.0 geben Diktiermethoden Ansichten zurück - warum?

Der Hauptgrund ist, dass für viele Anwendungsfälle die Rückgabe einer vollständig getrennten Liste unnötig und verschwenderisch ist. Es würde das Kopieren des gesamten Inhalts erfordern (was viel oder nicht viel sein kann).

Wenn Sie einfach über die Schlüssel iterieren möchten, ist das Erstellen einer neuen Liste nicht erforderlich. Und wenn Sie es tatsächlich als separate Liste (als Kopie) benötigen, können Sie diese Liste einfach aus der Ansicht erstellen.

Rajendran T.
quelle
6
Die Ansichtsmethoden geben Ansichtsobjekte zurück, die nicht der Listenschnittstelle entsprechen.
Matthew Trevor
5

Mit Ansichten können Sie auf die zugrunde liegende Datenstruktur zugreifen, ohne sie zu kopieren. Neben der Dynamik im Gegensatz zum Erstellen einer Liste ist inTest eine der nützlichsten Anwendungen . Angenommen, Sie möchten überprüfen, ob ein Wert im Diktat enthalten ist oder nicht (entweder Schlüssel oder Wert).

Option eins besteht darin, eine Liste der Schlüssel mit zu erstellen. dict.keys()Dies funktioniert, verbraucht aber offensichtlich mehr Speicher. Wenn das Diktat sehr groß ist? Das wäre verschwenderisch.

Mit können viewsSie die eigentliche Datenstruktur ohne Zwischenliste iterieren.

Verwenden wir Beispiele. Ich habe ein Diktat mit 1000 Schlüsseln mit zufälligen Zeichenfolgen und Ziffern und kist der Schlüssel, nach dem ich suchen möchte

large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. }

>>> len(large_d)
1000

# this is one option; It creates the keys() list every time, it's here just for the example
timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000)
13.748743600954867


# now let's create the list first; only then check for containment
>>> list_keys = large_d.keys()
>>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000)
8.874809793833492


# this saves us ~5 seconds. Great!
# let's try the views now
>>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000)
0.08828549011070663

# How about saving another 8.5 seconds?

Wie Sie sehen können, viewsteigert das iterierende Objekt die Leistung erheblich und reduziert gleichzeitig den Speicheraufwand. Sie sollten sie verwenden, wenn Sie Setähnliche Vorgänge ausführen müssen .

Hinweis : Ich verwende Python 2.7

Chen A.
quelle
In Python> = 3 wird meiner Meinung .keys()nach standardmäßig eine Ansicht zurückgegeben. Vielleicht möchten Sie das noch einmal überprüfen
Yolo Voe
1
Du hast recht. Python 3+ verwendet häufig Ansichtsobjekte anstelle von Listen. Es ist viel speichereffizienter
Chen A.
1
Dieses Timing Ergebnisse sehr sagen, aber zu prüfen , ob keiner der Schlüssel des Wörterbuchs ist large_dgemeint mit getan werden k in large_d, in Python, die wahrscheinlich im Wesentlichen so schnell wie eine Ansicht mit (in anderen Worten, k in large_d.keys()ist Pythonic nicht und soll avoided- wie es ist k in large_d.viewkeys()).
Eric O Lebigot
Vielen Dank für ein solides und nützliches Beispiel. k in large_dist eigentlich deutlich schneller als k in large_d.viewkeys(), so dass das wohl vermieden werden sollte, aber das macht sinnvoll für k in large_d.viewvalues().
Naught101