TL; DR:
Bitte beziehen Glossar : hash()
als Abkürzung verwendet , um Objekte zu vergleichen, wird ein Objekt als hashable wenn es zu anderen Objekten verglichen werden kann. Deshalb verwenden wir hash()
. Es ist auch für den Zugriff auf dict
und set
Elemente , die als implementiert sind resizable Hash - Tabellen in CPython .
Technische Überlegungen
- Normalerweise ist der Vergleich von Objekten (die mehrere Rekursionsstufen umfassen können) teuer.
- vorzugsweise ist die
hash()
Funktion eine Größenordnung (oder mehrere) weniger teuer.
- Das Vergleichen von zwei Hashes ist einfacher als das Vergleichen von zwei Objekten. Hier befindet sich die Verknüpfung.
Wenn Sie lesen, wie Wörterbücher implementiert werden , verwenden sie Hash-Tabellen. Das Ableiten eines Schlüssels von einem Objekt ist ein Eckpfeiler für das Abrufen von Objekten in Wörterbüchern in O(1)
. Dies hängt jedoch stark von Ihrer Hash-Funktion ab, um kollisionssicher zu sein . Der schlimmste Fall, um ein Element in ein Wörterbuch aufzunehmen, ist tatsächlich O(n)
.
In diesem Sinne sind veränderbare Objekte normalerweise nicht hashbar. Die Eigenschaft hashable bedeutet, dass Sie ein Objekt als Schlüssel verwenden können. Wenn der Hash-Wert als Schlüssel verwendet wird und sich der Inhalt desselben Objekts ändert, was sollte die Hash-Funktion dann zurückgeben? Ist es der gleiche oder ein anderer Schlüssel? Dies hängt davon ab, wie Sie Ihre Hash-Funktion definieren.
Mit gutem Beispiel lernen:
Stellen Sie sich vor, wir haben diese Klasse:
>>> class Person(object):
... def __init__(self, name, ssn, address):
... self.name = name
... self.ssn = ssn
... self.address = address
... def __hash__(self):
... return hash(self.ssn)
... def __eq__(self, other):
... return self.ssn == other.ssn
...
Bitte beachten Sie: Dies alles basiert auf der Annahme, dass sich die SSN für eine Person niemals ändert (Sie wissen nicht einmal, wo Sie diese Tatsache tatsächlich aus einer maßgeblichen Quelle überprüfen können).
Und wir haben Bob:
>>> bob = Person('bob', '1111-222-333', None)
Bob geht zu einem Richter, um seinen Namen zu ändern:
>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')
Das wissen wir:
>>> bob == jim
True
Dies sind jedoch zwei verschiedene Objekte mit unterschiedlichem Speicher, genau wie zwei verschiedene Datensätze derselben Person:
>>> bob is jim
False
Jetzt kommt der Teil, in dem hash () praktisch ist:
>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
Erraten Sie, was:
>>> dmv_appointments[jim]
'tomorrow'
Über zwei verschiedene Datensätze können Sie auf dieselben Informationen zugreifen. Versuchen Sie jetzt Folgendes:
>>> dmv_appointments[hash(jim)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in __eq__
AttributeError: 'int' object has no attribute 'ssn'
>>> hash(jim) == hash(hash(jim))
True
Was ist gerade passiert? Das ist eine Kollision. Da hash(jim) == hash(hash(jim))
es sich übrigens um ganze Zahlen handelt, müssen wir die Eingabe __getitem__
mit allen kollidierenden Elementen vergleichen . Das eingebaute int
hat kein ssn
Attribut und löst daher aus.
>>> del Person.__eq__
>>> dmv_appointments[bob]
'tomorrow'
>>> dmv_appointments[jim]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: <__main__.Person object at 0x7f611bd37110>
In diesem letzten Beispiel zeige ich, dass selbst bei einer Kollision der Vergleich durchgeführt wird und die Objekte nicht mehr gleich sind, was bedeutet, dass a erfolgreich ausgelöst wird KeyError
.