Ich bin etwas verwirrt darüber, was als Schlüssel für ein Python-Diktat verwendet werden kann / nicht.
dicked = {}
dicked[None] = 'foo' # None ok
dicked[(1,3)] = 'baz' # tuple ok
import sys
dicked[sys] = 'bar' # wow, even a module is ok !
dicked[(1,[3])] = 'qux' # oops, not allowed
Ein Tupel ist also ein unveränderlicher Typ, aber wenn ich eine Liste darin verstecke, kann es kein Schlüssel sein. Könnte ich eine Liste nicht genauso einfach in einem Modul verstecken?
Ich hatte eine vage Vorstellung davon, dass der Schlüssel "hashbar" sein muss, aber ich gebe nur meine eigene Unkenntnis über die technischen Details zu; Ich weiß nicht, was hier wirklich los ist. Was würde schief gehen, wenn Sie versuchen würden, Listen als Schlüssel zu verwenden, wobei der Hash beispielsweise deren Speicherort ist?
Antworten:
Es gibt einen guten Artikel zu diesem Thema im Python-Wiki: Warum Listen keine Wörterbuchschlüssel sein können . Wie dort erklärt:
Dies kann durchgeführt werden, ohne die Anforderungen wirklich zu verletzen, führt jedoch zu unerwartetem Verhalten. Listen werden im Allgemeinen so behandelt, als ob ihr Wert aus den Werten ihres Inhalts abgeleitet wurde, beispielsweise bei der Überprüfung der (Un-) Gleichheit. Viele würden - verständlicherweise - erwarten, dass Sie jede Liste verwenden können
[1, 2]
, um denselben Schlüssel zu erhalten, bei dem Sie genau dasselbe Listenobjekt behalten müssten . Die Suche nach Werten wird jedoch unterbrochen, sobald eine als Schlüssel verwendete Liste geändert wird. Für die Suche nach Identität müssen Sie genau dieselbe Liste verwenden - was für keine andere allgemeine Listenoperation erforderlich ist (zumindest keine, die mir einfällt) ).Andere Objekte wie Module und
object
machen sowieso viel mehr aus ihrer Objektidentität (wann haben Sie das letzte Mal zwei verschiedene Modulobjekte aufgerufensys
?) Und werden damit trotzdem verglichen. Daher ist es weniger überraschend - oder sogar zu erwarten -, dass sie, wenn sie als Diktatschlüssel verwendet werden, auch in diesem Fall nach Identität verglichen werden.quelle
Warum kann ich in Python keine Liste als Diktatschlüssel verwenden?
(für alle, die über diese Frage stolpern und nach einem Weg suchen, sie zu umgehen)
Wie von anderen hier erklärt, können Sie in der Tat nicht. Sie können jedoch stattdessen die Zeichenfolgendarstellung verwenden, wenn Sie Ihre Liste wirklich verwenden möchten.
quelle
__eq__
. Wenn Sie sie jedoch in Zeichenfolgen konvertieren, wird alles anhand der Zeichenfolgendarstellung verglichen.Gerade gefunden, können Sie List in Tupel ändern und dann als Schlüssel verwenden.
quelle
Das Problem ist, dass Tupel unveränderlich sind und Listen nicht. Folgendes berücksichtigen
Was soll
d[li]
zurückkehren? Ist es die gleiche Liste? Wie wäre es mitd[[1,2,3]]
? Es hat die gleichen Werte, aber ist eine andere Liste?Letztendlich gibt es keine zufriedenstellende Antwort. Wenn der einzige Schlüssel, der funktioniert, beispielsweise der Originalschlüssel ist, können Sie nie wieder auf den Wert zugreifen, wenn Sie keinen Verweis auf diesen Schlüssel haben. Mit jedem anderen zulässigen Schlüssel können Sie einen Schlüssel ohne Verweis auf das Original erstellen.
Wenn meine beiden Vorschläge funktionieren, haben Sie sehr unterschiedliche Schlüssel, die denselben Wert zurückgeben, was mehr als überraschend ist. Wenn nur der ursprüngliche Inhalt funktioniert, wird Ihr Schlüssel schnell fehlerhaft, da Listen geändert werden müssen.
quelle
d[li]
, dass sie bleibt. 5.d[[1,2,3]]
würde sich auf ein anderes Listenobjekt als Schlüssel beziehen, also wäre es ein KeyError. Ich sehe noch kein wirkliches Problem. Abgesehen davon, dass das Sammeln von Müll durch einen Schlüssel dazu führen kann, dass einige der Diktatwerte nicht mehr zugänglich sind. Aber das ist ein praktisches Problem, kein logisches Problem.d[list(li)]
Ein KeyError zu sein ist Teil des Problems. In fast jedem anderen Anwendungsfall ,li
würde von einer neuen Liste mit identischem Inhalt nicht zu unterscheiden sein. Es funktioniert, ist aber für viele nicht intuitiv. Und wann mussten Sie das letzte Mal wirklich eine Liste als Diktatschlüssel verwenden? Der einzige Anwendungsfall, den ich mir vorstellen kann, ist, wenn Sie sowieso alles nach Identität durchsuchen. In diesem Fall sollten Sie dies einfach tun, anstatt sich auf identitätsbasiert zu verlassen__hash__
und__eq__
auf dieser zu basieren.Hier ist eine Antwort http://wiki.python.org/moin/DictionaryKeys
Das Nachschlagen verschiedener Listen mit demselben Inhalt würde zu unterschiedlichen Ergebnissen führen, obwohl ein Vergleich von Listen mit demselben Inhalt sie als gleichwertig anzeigen würde.
Was ist mit der Verwendung eines Listenliteral in einer Wörterbuchsuche?
quelle
Ihre Markise finden Sie hier:
Quelle und weitere Informationen: http://wiki.python.org/moin/DictionaryKeys
quelle
Da Listen veränderbar sind, müssen
dict
Schlüssel (undset
Mitglieder) hashbar sein, und das Hashing veränderbarer Objekte ist eine schlechte Idee, da Hashwerte auf der Grundlage von Instanzattributen berechnet werden sollten .In dieser Antwort werde ich einige konkrete Beispiele geben, die hoffentlich zusätzlich zu den vorhandenen Antworten einen Mehrwert bieten. Jede Einsicht gilt auch für die Elemente der
set
Datenstruktur.Beispiel 1 : Hashing eines veränderlichen Objekts, wobei der Hash-Wert auf einer veränderlichen Eigenschaft des Objekts basiert.
Nach der Mutation
stupid
kann es nicht mehr im Diktat gefunden werden, da sich der Hash geändert hat. Nur ein linearer Scan über die Liste der Schlüssel des Diktats findetstupid
.Beispiel 2 : ... aber warum nicht einfach ein konstanter Hashwert?
Das ist auch keine gute Idee, da gleiche Objekte identisch hashen sollten, damit Sie sie in einem
dict
oder finden könnenset
.Beispiel 3 : ... ok, was ist mit konstanten Hashes über alle Instanzen hinweg?!
Die Dinge scheinen wie erwartet zu funktionieren, aber denken Sie darüber nach, was passiert: Wenn alle Instanzen Ihrer Klasse denselben Hash-Wert erzeugen, kommt es immer dann zu einer Hash-Kollision, wenn mehr als zwei Instanzen als Schlüssel in einem
dict
oder in einem vorhanden sindset
.Um die richtige Instanz mit
my_dict[key]
oderkey in my_dict
(oderitem in my_set
) zu finden, müssen so viele Gleichheitsprüfungen durchgeführt werden, wie Instanzenstupidlist3
in den Schlüsseln des Diktats vorhanden sind (im schlimmsten Fall). Zu diesem Zeitpunkt ist der Zweck des Wörterbuchs - O (1) Lookup - vollständig besiegt. Dies wird in den folgenden Timings (mit IPython) demonstriert.Einige Timings für Beispiel 3
Wie Sie sehen können, ist der Mitgliedschaftstest in unserem
stupidlists_set
System sogar noch langsamer als ein linearer Scanlists_list
, während Sie die erwartete superschnelle Suchzeit (Faktor 500) in einem Satz ohne viele Hash-Kollisionen haben.TL; DR: Sie können
tuple(yourlist)
alsdict
Schlüssel verwenden, da Tupel unveränderlich und hashbar sind.quelle
x
undz
ist der gleiche. Wenn etwas darüber unklar ist, öffnen Sie bitte eine neue Frage.hash(x)
undhash(z)
.Die einfache Antwort auf Ihre Frage lautet, dass die Klassenliste nicht den Methoden- Hash implementiert, der für ein Objekt erforderlich ist, das als Schlüssel in einem Wörterbuch verwendet werden soll. Der Grund, warum Hash nicht so implementiert wird wie beispielsweise die Tupelklasse (basierend auf dem Inhalt des Containers), liegt darin, dass eine Liste veränderbar ist, sodass für die Bearbeitung der Liste der Hash neu berechnet werden muss, was bedeuten kann, dass die Liste in befindet sich jetzt im falschen Eimer in der Untergrund-Hash-Tabelle. Da Sie ein Tupel (unveränderlich) nicht ändern können, tritt dieses Problem nicht auf.
Nebenbei bemerkt basiert die tatsächliche Implementierung der Diktobjektsuche auf dem Algorithmus D von Knuth Vol. 1. 3, Sec. 6.4. Wenn Sie dieses Buch zur Verfügung haben, ist es möglicherweise eine lohnende Lektüre. Wenn Sie wirklich, wirklich interessiert sind, können Sie hier einen Blick auf die Entwicklerkommentare zur tatsächlichen Implementierung von dictobject werfen. Es wird sehr detailliert beschrieben, wie es genau funktioniert. Es gibt auch eine Python-Vorlesung über die Implementierung von Wörterbüchern, an denen Sie interessiert sein könnten. Sie gehen die Definition eines Schlüssels durch und was ein Hash in den ersten Minuten ist.
quelle
Gemäß der Python 2.7.2-Dokumentation:
Ein Tupel ist in dem Sinne unveränderlich, dass Sie seine Elemente nicht hinzufügen, entfernen oder ersetzen können, aber die Elemente selbst können veränderlich sein. Der Hash-Wert von List hängt von den Hash-Werten seiner Elemente ab und ändert sich daher, wenn Sie die Elemente ändern.
Die Verwendung von IDs für Listen-Hashes würde bedeuten, dass alle Listen unterschiedlich verglichen werden, was überraschend und unpraktisch wäre.
quelle
hash = id
bricht die Invariante am Ende des ersten Absatzes nicht, die Frage ist, warum es nicht so gemacht wird.so etwas wie (Pseudo-Code):
Wenn Sie sich fragen, welche Optionen verfügbar sind, die als Schlüssel für Ihr Wörterbuch verwendet werden können. Dann
Du kannst es versuchen :
Wenn es gut funktioniert, kann es als Schlüssel für Ihr Wörterbuch verwendet oder in etwas Hashbares konvertiert werden.
Zusamenfassend :
tuple(<your list>)
.str(<your list>)
.quelle
dict
Schlüssel müssen hashbar sein. Listen sind veränderlich und bieten keine gültige Hash- Methode.quelle