Ist ein Python-Wörterbuch ein Beispiel für eine Hash-Tabelle?

186

Eine der grundlegenden Datenstrukturen in Python ist das Wörterbuch, mit dem "Schlüssel" zum Nachschlagen von "Werten" eines beliebigen Typs aufgezeichnet werden können. Wird dies intern als Hash-Tabelle implementiert? Wenn nicht, was ist das?

Tommy Herbert
quelle
2
Wenn Sie an den technischen Details interessiert sind, befasst sich ein Artikel in Beautiful Code mit den Interna der Python- dictImplementierung.
Torsten Marek
Das war eines meiner Lieblingskapitel in Beautiful Code.
DGentry
4
Hier ist ein Vortrag von Brandon Craig Rhodes über die Funktionsweise des Python-Wörterbuchs unter youtube.com/watch?v=C4Kc8xzcA68 .
Chandola
Ich suchte seit einiger Zeit nach einem Diagramm, das ein Diktat darstellt, das die Implementierung in Speicher und CPython beschreibt. Vielen Dank, dass Sie auf das Buch verwiesen haben!
Chen A.

Antworten:

238

Ja, es handelt sich um eine Hash-Zuordnung oder eine Hash-Tabelle. Eine Beschreibung der Diktatimplementierung von Python, wie sie von Tim Peters geschrieben wurde, finden Sie hier .

Aus diesem Grund können Sie etwas, das nicht hashbar ist, nicht als Diktatschlüssel verwenden, z. B. eine Liste:

>>> a = {}
>>> b = ['some', 'list']
>>> hash(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list objects are unhashable
>>> a[b] = 'some'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list objects are unhashable

Sie können mehr über Hash-Tabellen lesen oder überprüfen, wie sie in Python implementiert wurden und warum sie auf diese Weise implementiert wurden .

nosklo
quelle
1
Die Verbindungsnähte von Tim Peters müssen gebrochen werden. Gibt es da draußen eine saubere Verbindung?
Matt Alcock
1
@ MattAlcock: Ich habe den Link aktualisiert. Manchmal (normalerweise, weil jemand möchte, dass seine E-Mail-Adresse irgendwo entfernt wird) werden die Python-Listenarchive neu erstellt und die IDs von E-Mails ändern sich, wodurch diese Links unterbrochen werden. Die Pydotorg-Administratoren versuchen heutzutage im Allgemeinen, dies zu vermeiden.
Martijn Pieters
Mit .keys()kann jedoch eine Liste von Schlüsseln abgerufen werden. Eine echte Hash-Tabelle würde keine Schlüssel speichern, sondern nur Hashes, um Platz zu sparen.
noɥʇʎԀʎzɐɹƆ
Eine ausführlichere
Daniel Goldfarb
31

Ein Python-Wörterbuch muss mehr enthalten als eine Tabellensuche in hash (). Durch brutale Experimente fand ich diese Hash-Kollision :

>>> hash(1.1)
2040142438
>>> hash(4504.1)
2040142438

Dennoch bricht es das Wörterbuch nicht:

>>> d = { 1.1: 'a', 4504.1: 'b' }
>>> d[1.1]
'a'
>>> d[4504.1]
'b'

Gesundheitsüberprüfung:

>>> for k,v in d.items(): print(hash(k))
2040142438
2040142438

Möglicherweise gibt es neben hash () eine weitere Nachschlageebene, die Kollisionen zwischen Wörterbuchschlüsseln vermeidet. Oder vielleicht verwendet dict () einen anderen Hash.

(Übrigens in Python 2.7.10. Gleiche Geschichte in Python 3.4.3 und 3.5.0 mit einer Kollision bei hash(1.1) == hash(214748749.8).)

Bob Stein
quelle
14
Kollisionen sind also unvermeidlich. Set S kann eine unendlich große Anzahl von Elementen enthalten, und Sie möchten, dass es auf eine Nummer gehasht wird, die ein Computer speichern kann. Jede verwendbare Implementierung einer Hash-Tabelle löst Kollisionen auf, wobei zwei der häufigsten Methoden a) offene Adressierung und b) Verkettung sind. Nur weil es keinen perfekten Hash verwendet, heißt das nicht, dass es keine Hash-Tabelle ist.
TurnipEntropy
1
Kollisionen treten im Allgemeinen auf, da es unendlich viele mögliche Hashwerte und endliche Hashcodes gibt. Sogar eine Hash-Tabelle müsste irgendwie mit Kollisionen umgehen.
Yanfeng Liu
3
@YanfengLiu Ich glaube, das sind genau die gleichen Punkte, die TurnipEntropy gemacht hat.
Bob Stein
1
In Python 3.7 scheint es tatsächlich 2E20 minus 1 mögliche Hashwerte zu geben. Von -1E20 minus 1 bis (+) 1E20 minus 1. Versuch hash('I wandered lonely as a cloud, that drifts on high o\'er vales and hills, when all at once, I saw a crowd, a host of golden daffodils.')Dies ergibt eine 19-stellige Dezimalstelle - -4037225020714749784wenn Sie geeky genug sind, um sich darum zu kümmern. Fahren Sie mit Ihren eigenen Worten fort, Kinder, und der Hash ist immer noch eine 19-stellige Zahl. Ich gehe davon aus, dass die Länge der Zeichenfolge, die Sie in Python hashen können, begrenzt ist, aber sicher viel mehr mögliche Zeichenfolgen als mögliche Werte. Und hash(False)= 0 übrigens.
Will Croxford
22

Ja. Intern wird es als offenes Hashing basierend auf einem primitiven Polynom über Z / 2 ( Quelle ) implementiert .

Ben Hoffstein
quelle
7

Um die Erklärung von nosklo zu erweitern:

a = {}
b = ['some', 'list']
a[b] = 'some' # this won't work
a[tuple(b)] = 'some' # this will, same as a['some', 'list']
Jeremy Cantrell
quelle