Beim Hashing werden einige große Datenmengen auf wiederholbare Weise in eine viel kleinere Datenmenge (normalerweise eine einzelne Ganzzahl) konvertiert, damit sie in konstanter Zeit ( O(1)
) in einer Tabelle nachgeschlagen werden können , was für eine hohe Leistung wichtig ist Algorithmen und Datenstrukturen.
Unveränderlichkeit ist die Idee, dass sich ein Objekt nach seiner Erstellung nicht auf eine wichtige Weise ändert, insbesondere nicht auf eine Weise, die den Hash-Wert dieses Objekts ändern könnte.
Die beiden Ideen hängen zusammen, da Objekte, die als Hash-Schlüssel verwendet werden, normalerweise unveränderlich sein müssen, damit sich ihr Hash-Wert nicht ändert. Wenn es erlaubt wäre, sich zu ändern, würde sich die Position dieses Objekts in einer Datenstruktur wie einer Hashtabelle ändern, und dann wird der gesamte Zweck des Hashing aus Effizienzgründen zunichte gemacht.
Um die Idee wirklich zu verstehen, sollten Sie versuchen, Ihre eigene Hashtabelle in einer Sprache wie C / C ++ zu implementieren, oder die Java-Implementierung der HashMap
Klasse lesen .
HashMap
wird, wenn Sie ein Objekt ändern, das als Schlüssel darin verwendet wird: Es kann weder ein alter noch ein neuer Schlüssel gefunden werden, obwohl er dort angezeigt wird, wenn Sie die Karte drucken.object
sind hashbar, aber nicht unveränderlich. Diese Instanzen können verwendet werden, haben Schlüssel eines Diktats, können aber trotzdem geändert werden, wenn sie weitergegeben werden.In Python ist Tupel unveränderlich, aber nur dann hashbar, wenn alle Elemente hashbar sind.
>>> tt = (1, 2, (30, 40)) >>> hash(tt) 8027212646858338501 >>> tl = (1, 2, [30, 40]) >>> hash(tl) TypeError: unhashable type: 'list'
Hashbare Typen
quelle
Aus dem Python-Glossar :
Dicts und Sets müssen einen Hash für eine effiziente Suche in einer Hash-Tabelle verwenden. Die Hash-Werte müssen unveränderlich sein, da das Ändern des Hash die Datenstrukturen durcheinander bringt und dazu führt, dass das Diktat oder Set fehlschlägt. Der einfachste Weg, den Hash-Wert unveränderlich zu machen, besteht darin, das gesamte Objekt unveränderlich zu machen, weshalb die beiden häufig zusammen erwähnt werden.
Während keines der integrierten veränderbaren Objekte hashbar ist, ist es möglich, ein veränderbares Objekt mit einem Hash-Wert zu erstellen, der nicht veränderbar ist. Es ist üblich, dass nur ein Teil des Objekts seine Identität darstellt, während der Rest des Objekts Eigenschaften enthält, die sich frei ändern können. Solange der Hashwert und die Vergleichsfunktionen auf der Identität, aber nicht auf den veränderlichen Eigenschaften basieren und sich die Identität nie ändert, haben Sie die Anforderungen erfüllt.
quelle
id
). Dies kann sich während der Lebensdauer des Objekts nicht ändern, ist also hashbar, aber das bedeutet nicht, dass Sie keine veränderlichen Typen definieren können! Sorry, aber Hashability bedeutet nicht Unveränderlichkeit.Hashable bedeutet technisch gesehen, dass die Klasse definiert
__hash__()
. Laut den Dokumenten:Ich denke, dass für die in Python eingebauten Typen alle Hash-Typen ebenfalls unveränderlich sind.
Es wäre schwierig, aber vielleicht nicht unmöglich, ein veränderliches Objekt zu haben, das dennoch definiert ist
__hash__()
.quelle
__hash__
standardmäßig definiert ist, um die Objekte zurückzugebenid
. Sie müssen sich alle__hash__ = None
Mühe geben, um es nicht zerlegbar zu machen. Wie Mark Ransom erwähnt, gibt es eine zusätzliche Bedingung, dass es nur hashbar ist, wenn sich der Hashwert niemals ändern kann!list
definiert__hash__
in dem Sinne, dasshasattr([1,2,3], "__hash__")
zurückgegeben wirdTrue
, jedoch rufthash([1,2,3])
das Aufrufen einTypeError
(Python 3) auf, so dass es nicht genau hashbar ist. Sich auf die Existenz von zu verlassen,__hash__
reicht nicht aus, um festzustellen, ob etwas a) hashbar b) unveränderlich istEs gibt eine implizite Beziehung, auch wenn aufgrund des Zusammenspiels zwischen beiden keine explizite Beziehung zwischen unveränderlich und hashbar erzwungen wird
Hier gibt es kein Problem, es sei denn, Sie definieren neu,
__eq__
sodass die Objektklasse die Äquivalenz für den Wert definiert.Sobald Sie dies getan haben, müssen Sie eine stabile Hash-Funktion finden, die immer den gleichen Wert für Objekte zurückgibt, die den gleichen Wert darstellen (z. B. wo
__eq__
) True zurückgibt und sich während der Lebensdauer eines Objekts nie ändert.Es ist schwer, eine Anwendung zu finden, in der dies möglich ist. Betrachten Sie eine mögliche Klasse A, die diese Anforderungen erfüllt. Obwohl es den offensichtlichen entarteten Fall
__hash__
gibt, in dem eine Konstante zurückgegeben wird.Jetzt:-
>>> a = A(1) >>> b = A(1) >>> c = A(2) >>> a == b True >>> a == c False >>> hash(a) == hash(b) True >>> a.set_value(c) >>> a == c True >>> assert(hash(a) == hash(c)) # Because a == c => hash(a) == hash(c) >>> assert(hash(a) == hash(b)) # Because hash(a) and hash(b) have compared equal before and the result must stay static over the objects lifetime.
Tatsächlich bedeutet dies bei der Erstellung Hash (b) == Hash (c), obwohl es nie gleich verglichen wird. Ich habe sowieso Schwierigkeiten zu sehen, um sinnvoll
__hash__
() für ein veränderliches Objekt zu definieren, das Vergleich nach Wert definiert.Hinweis :
__lt__
,__le__
,__gt__
und__ge__
comparsions sind davon nicht betroffen , so dass Sie immer noch eine Bestellung von hashable Objekten, wandelbar oder auf andere Weise , basierend auf ihrem Wert definieren.quelle
Nur weil dies der Top-Hit von Google ist, gibt es eine einfache Möglichkeit, ein veränderbares Objekt hashbar zu machen:
>>> class HashableList(list): ... instancenumber = 0 # class variable ... def __init__(self, initial = []): ... super(HashableList, self).__init__(initial) ... self.hashvalue = HashableList.instancenumber ... HashableList.instancenumber += 1 ... def __hash__(self): ... return self.hashvalue ... >>> l = [1,2,3] >>> m = HashableList(l) >>> n = HashableList([1,2,3]) >>> m == n True >>> a={m:1, n:2} >>> a[l] = 3 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list' >>> m.hashvalue, n.hashvalue (0, 1)
Ich habe tatsächlich eine Verwendung für so etwas gefunden, als ich eine Klasse erstellt habe, um SQLAlchemy-Datensätze in etwas für mich veränderliches und nützlicheres umzuwandeln, während ihre Hashfähigkeit für die Verwendung als Diktatschlüssel beibehalten wurde.
quelle
Unveränderlich bedeutet, dass sich das Objekt während seiner Lebensdauer nicht wesentlich verändert. Es ist eine vage, aber verbreitete Idee in Programmiersprachen.
Die Hashability unterscheidet sich geringfügig und bezieht sich auf den Vergleich.
Alle benutzerdefinierten Klassen verfügen über eine
__hash__
Methode, die standardmäßig nur die Objekt-ID zurückgibt. Ein Objekt, das die Kriterien für die Hashfähigkeit erfüllt, ist also nicht unbedingt unveränderlich.Objekte einer neuen Klasse, die Sie deklarieren, können als Wörterbuchschlüssel verwendet werden, es sei denn, Sie verhindern dies, indem Sie beispielsweise von werfen
__hash__
Wir könnten sagen, dass alle unveränderlichen Objekte hashbar sind, denn wenn sich der Hash während der Lebensdauer des Objekts ändert, bedeutet dies, dass das Objekt mutiert ist.
Aber nicht ganz. Stellen Sie sich ein Tupel mit einer Liste vor (veränderbar). Einige sagen, Tupel sei unveränderlich, aber gleichzeitig ist es etwas nicht hashbar (Würfe).
d = dict() d[ (0,0) ] = 1 #perfectly fine d[ (0,[0]) ] = 1 #throws
Hashability und Unveränderlichkeit beziehen sich auf Objektinstanz, nicht auf Typ. Beispielsweise kann ein Objekt vom Typ Tupel hashbar sein oder nicht.
quelle
comparison != identity
denen "ungültige" Werte wiefloat("nan") == float("nan")
oder internierte Zeichenfolgen aus Slices"apple" is "apple"
"apple" is "crabapple"[4:]
In Python sind sie meistens austauschbar. Da der Hash den Inhalt darstellen soll, ist er genauso veränderlich wie das Objekt, und wenn ein Objekt geändert wird, würde der Hash-Wert ihn als Diktatschlüssel unbrauchbar machen.
In anderen Sprachen bezieht sich der Hash-Wert eher auf die 'Identität' der Objekte und nicht (notwendigerweise) auf den Wert. Somit könnte für ein veränderliches Objekt der Zeiger verwendet werden, um das Hashing zu starten. Vorausgesetzt natürlich, dass sich ein Objekt nicht im Speicher bewegt (wie es einige GC tun). Dies ist beispielsweise der in Lua verwendete Ansatz. Dadurch kann ein veränderbares Objekt als Tabellenschlüssel verwendet werden. schafft aber einige (unangenehme) Überraschungen für Neulinge.
Am Ende macht es ein unveränderlicher Sequenztyp (Tupel) für 'mehrwertige Schlüssel' schöner.
quelle
Hashable bedeutet, dass der Wert einer Variablen durch eine Konstante dargestellt (oder vielmehr codiert) werden kann - Zeichenfolge, Zahl usw. Nun kann etwas, das geändert werden kann (veränderlich), nicht durch etwas dargestellt werden, das nicht geändert wird. Daher kann jede Variable, die veränderbar ist, nicht hashbar sein, und aus dem gleichen Grund können nur unveränderliche Variablen hashbar sein.
Hoffe das hilft ...
quelle