Un dictionnaire Python est-il un exemple de table de hachage?

200

L'une des structures de données de base en Python est le dictionnaire, qui permet d'enregistrer des «clés» pour rechercher des «valeurs» de tout type. Est-ce implémenté en interne sous forme de table de hachage? Sinon, qu'est-ce que c'est?

Tommy Herbert
la source
4
Si vous êtes intéressé par les détails techniques, un article de Beautiful Code traite des éléments internes de l' dictimplémentation de Python .
Torsten Marek
C'était l'un de mes chapitres préférés de Beautiful Code.
DGentry le
5
Voici une conférence de Brandon Craig Rhodes sur le fonctionnement du dictionnaire python, youtube.com/watch?v=C4Kc8xzcA68 .
chandola
J'ai cherché un diagramme représentant un dict depuis un moment maintenant, qui décrypte l'implémentation en mémoire et CPython. Merci d'avoir référencé le livre!
Chen A.

Réponses:

254

Oui, c'est un mappage de hachage ou une table de hachage. Vous pouvez lire une description de l'implémentation de dict de python, écrite par Tim Peters, ici .

C'est pourquoi vous ne pouvez pas utiliser quelque chose de 'non hachable' comme clé de dict, comme une 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

Vous pouvez en savoir plus sur les tables de hachage ou vérifier comment elles ont été implémentées en python et pourquoi elles sont implémentées de cette façon .

nosklo
la source
1
Les coutures du lien Tim Peters à briser, y a-t-il un lien propre?
Matt Alcock
1
@MattAlcock: J'ai mis à jour le lien. Parfois (généralement parce que quelqu'un souhaite que son adresse e-mail soit supprimée quelque part), les archives de la liste Python sont reconstruites et les identifiants des e-mails changent, rompant ainsi ces liens. Les administrateurs de pydotorg essaient généralement d'éviter cela de nos jours.
Martijn Pieters
Mais l'utilisation .keys()peut récupérer une liste de clés. Une vraie table de hachage ne stockerait pas les clés, juste des hachages pour économiser de l'espace.
noɥʇʎԀʎzɐɹƆ
Description plus complète de l'implémentation de python dict ici: laurentluce.com/posts/python-dictionary-implementation
Daniel Goldfarb
@ noɥʇʎԀʎzɐɹƆ - la clé elle-même n'est pas stockée, seulement une référence à elle et le hachage.
nosklo
34

Il doit y avoir plus dans un dictionnaire Python qu'une recherche de table sur hash (). Par expérimentation brute, j'ai trouvé cette collision de hachage :

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

Pourtant, cela ne casse pas le dictionnaire:

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

Verification sanitaire:

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

Il existe peut-être un autre niveau de recherche au-delà de hash () qui évite les collisions entre les clés du dictionnaire. Ou peut-être que dict () utilise un hachage différent.

(Au fait, ceci en Python 2.7.10. Même histoire en Python 3.4.3 et 3.5.0 avec une collision à hash(1.1) == hash(214748749.8).)

Bob Stein
la source
15
Les collisions sont donc inévitables. L'ensemble S peut contenir un nombre infiniment grand d'éléments et vous souhaitez qu'il hache en un nombre qu'un ordinateur peut stocker. Chaque implémentation utilisable d'une table de hachage résout les collisions, deux des méthodes les plus fréquentes étant a) l'adressage ouvert et b) le chaînage. Ce n'est pas parce qu'il n'utilise pas un hachage parfait que ce n'est pas une table de hachage.
TurnipEntropy
1
Les collisions se produiront en général, car il existe une infinité de valeurs hachables et de codes de hachage finis. Même une table de hachage devrait gérer une collision d'une manière ou d'une autre.
Yanfeng Liu
3
@YanfengLiu Je crois que ce sont exactement les mêmes points que TurnipEntropy.
Bob Stein
2
Dans Python 3.7, il semble qu'il y ait 2E20 moins 1 valeurs de hachage possibles, en fait. De -1E20 moins 1 à (+) 1E20 moins 1. Essayez 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.')Cela donne une décimale à 19 chiffres - -4037225020714749784si vous êtes assez geek pour s'en soucier. Continuez avec vos propres mots, enfants, et le hachage est toujours un nombre à 19 chiffres. Je suppose qu'il existe une limite de longueur de chaîne que vous pouvez hacher en Python, mais il est prudent de dire beaucoup plus de chaînes possibles que de valeurs possibles. Et hash(False)= 0 au fait.
Will Croxford
22

Oui. En interne, il est implémenté sous forme de hachage ouvert basé sur un polynôme primitif sur Z / 2 ( source ).

Ben Hoffstein
la source
7

Pour développer l'explication de nosklo:

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