TL; DR:
Consulte el glosario : hash()se utiliza como un atajo para comparar objetos, un objeto se considera hash si se puede comparar con otros objetos. por eso usamos hash(). También se utiliza para el acceso dicty setelementos que se implementan como tablas hash de tamaño variable en CPython .
Consideraciones tecnicas
- Por lo general, comparar objetos (que pueden implicar varios niveles de recursividad) es caro.
- preferiblemente, la
hash()función es un orden de magnitud (o varios) menos costosa.
- comparar dos hashes es más fácil que comparar dos objetos, aquí es donde está el atajo.
Si lee sobre cómo se implementan los diccionarios , estos usan tablas hash, lo que significa que derivar una clave de un objeto es una piedra angular para recuperar objetos en diccionarios en formato O(1). Sin embargo, eso depende mucho de su función hash para ser resistente a las colisiones . El peor caso para obtener un elemento en un diccionario es en realidad O(n).
En esa nota, los objetos mutables generalmente no son hash. La propiedad hash significa que puede usar un objeto como clave. Si el valor hash se utiliza como clave y el contenido de ese mismo objeto cambia, ¿qué debería devolver la función hash? ¿Es la misma clave o una diferente? Que depende de cómo se defina su función hash.
Aprendiendo con el ejemplo:
Imagina que tenemos esta clase:
>>> 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
...
Tenga en cuenta: todo esto se basa en la suposición de que el SSN nunca cambia para un individuo (ni siquiera sé dónde verificar ese hecho de una fuente autorizada).
Y tenemos a Bob:
>>> bob = Person('bob', '1111-222-333', None)
Bob va a ver a un juez para cambiar su nombre:
>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')
Esto es lo que sabemos:
>>> bob == jim
True
Pero estos son dos objetos diferentes con diferente memoria asignada, al igual que dos registros diferentes de la misma persona:
>>> bob is jim
False
Ahora viene la parte donde hash () es útil:
>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
Adivina qué:
>>> dmv_appointments[jim]
'tomorrow'
Desde dos registros diferentes puede acceder a la misma información. Ahora prueba esto:
>>> 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
¿Lo que acaba de suceder? Eso es una colisión. Debido a hash(jim) == hash(hash(jim))que ambos son enteros por cierto, necesitamos comparar la entrada de __getitem__con todos los elementos que colisionan. El incorporado intno tiene ningún ssnatributo, por lo que se dispara.
>>> 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>
En este último ejemplo, muestro que incluso con una colisión, se realiza la comparación, los objetos ya no son iguales, lo que significa que sube con éxito a KeyError.