¿Es un diccionario de Python un ejemplo de una tabla hash?

187

Una de las estructuras básicas de datos en Python es el diccionario, que permite registrar "claves" para buscar "valores" de cualquier tipo. ¿Se implementa esto internamente como una tabla hash? Si no, ¿qué es?

Tommy Herbert
fuente
2
Si está interesado en los detalles técnicos, un artículo en Beautiful Code trata de los aspectos internos de la dictimplementación de Python .
Torsten Marek
Ese fue uno de mis capítulos favoritos en Beautiful Code.
DGentry
44
Aquí hay una charla de Brandon Craig Rhodes sobre cómo funciona el diccionario python, youtube.com/watch?v=C4Kc8xzcA68 .
chandola
Busqué un diagrama que representa un dict por un tiempo ahora, que descifra la implementación en memoria y CPython. ¡Gracias por hacer referencia al libro!
Chen A.

Respuestas:

241

Sí, es un mapeo hash o una tabla hash. Puede leer una descripción de la implementación dict de python, como está escrita por Tim Peters, aquí .

Es por eso que no puede usar algo 'no hashable' como clave dict, como una lista:

>>> 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

Puede leer más sobre las tablas hash o verificar cómo se ha implementado en python y por qué se implementa de esa manera .

nosklo
fuente
1
El enlace de Tim Peters parece estar roto, ¿hay un enlace limpio por ahí?
Matt Alcock
1
@MattAlcock: He actualizado el enlace. A veces (generalmente debido a que alguien quiere que se elimine su dirección de correo electrónico en algún lugar), los archivos de la lista de Python se reconstruyen y los identificadores de los correos electrónicos cambian, rompiendo así estos enlaces. Los administradores de pydotorg generalmente intentan evitar eso en estos días.
Martijn Pieters
Pero el uso .keys()puede recuperar una lista de claves. Una tabla hash real no almacenaría claves, solo hashes para ahorrar espacio.
noɥʇʎԀʎzɐɹƆ
Descripción más completa de la implementación de dict python aquí: laurentluce.com/posts/python-dictionary-implementation
Daniel Goldfarb
@ noɥʇʎԀʎzɐɹƆ: la clave en sí no se almacena, solo una referencia a ella y al hash.
nosklo hace
32

Debe haber más en un diccionario de Python que una búsqueda de tabla en hash (). Por experimentación bruta encontré esta colisión de hash :

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

Sin embargo, no rompe el diccionario:

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

Prueba de cordura:

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

Posiblemente hay otro nivel de búsqueda más allá de hash () que evita colisiones entre las teclas del diccionario. O tal vez dict () usa un hash diferente.

(Por cierto, esto en Python 2.7.10. La misma historia en Python 3.4.3 y 3.5.0 con una colisión en hash(1.1) == hash(214748749.8).)

Bob Stein
fuente
14
Entonces las colisiones son inevitables. El conjunto S puede contener una cantidad infinitamente grande de elementos, y usted desea que se mezcle con un número que una computadora puede almacenar. Cada implementación utilizable de una tabla hash resuelve colisiones, siendo dos de los métodos más frecuentes a) direccionamiento abierto y b) encadenamiento. El hecho de que no utilice un hash perfecto no significa que no sea una tabla hash.
TurnipEntropy
1
Las colisiones sucederán en general, porque hay infinitos valores hashables posibles y códigos hash finitos. Incluso una tabla hash tendría que manejar la colisión de alguna manera.
Yanfeng Liu
3
@ YanfengLiu Creo que esos son exactamente los mismos puntos que TurnipEntropy hizo.
Bob Stein
1
En Python 3.7, parece que hay 2E20 menos 1 valores hash posibles, de hecho. De -1E20 menos 1 a (+) 1E20 menos 1. Prueba 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.')Esto da un decimal de 19 dígitos, -4037225020714749784si eres lo suficientemente geek como para preocuparte. Continúen con sus propias palabras, niños, y el hash sigue siendo un número de 19 dígitos. Supongo que hay un límite en la longitud de la cadena que puede hacer hash en Python, pero es seguro decir muchas más cadenas posibles que valores posibles. Y hash(False)= 0 por cierto.
Will Croxford
22

Si. Internamente se implementa como hashing abierto basado en un polinomio primitivo sobre Z / 2 ( fuente ).

Ben Hoffstein
fuente
7

Para ampliar la explicación 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
fuente