Hashable, inmutable


A partir de una pregunta SO reciente (ver Crear un diccionario en python que está indexado por listas) me di cuenta de que probablemente tenía una concepción equivocada del significado de los objetos hashables e inmutables en python.

  • ¿Qué significa hashable en la práctica?
  • ¿Cuál es la relación entre hashable e inmmutable?
  • hay objetos mutables que se hashable o objetos inmutables que no son hashable?
Author: Community, 2010-04-20

9 answers

Hash es el proceso de convertir una gran cantidad de datos en una cantidad mucho más pequeña (típicamente un entero único) de una manera repetible para que pueda ser buscado en una tabla en tiempo constante (O(1)), que es importante para algoritmos de alto rendimiento y estructuras de datos.

Inmutabilidad es la idea de que un objeto no cambiará de alguna manera importante después de haber sido creado, especialmente en cualquier forma que pueda cambiar el valor hash de ese objeto.

Las dos ideas están relacionadas porque los objetos que se usan como claves hash normalmente deben ser inmutables para que su valor hash no cambie. Si se le permitiera cambiar, entonces la ubicación de ese objeto en una estructura de datos como una tabla hashtable cambiaría y entonces todo el propósito de hashing para la eficiencia es derrotado.

Para comprender realmente la idea, debe intentar implementar su propia tabla hash en un lenguaje como C/C++, o leer la implementación Java del HashMap clase.

 78
Author: maerics,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-19 22:39:41

Técnicamente, hashable significa que la clase define __hash__(). Según los documentos:

__hash__() debe devolver un entero. La única propiedad requerida es que los objetos que comparan equal tienen el mismo valor hash; se aconseja mezclar de alguna manera (por ejemplo, usando exclusive or) los valores hash para los componentes del objeto que también juegan un papel en la comparación de objetos.

Creo que para los tipos incorporados de python, todos los tipos hash también son inmutable. Sería difícil, pero quizás no imposible, tener un objeto mutable que definiera _ _ hash _ _ ().

 7
Author: Andrew Jaffe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-19 22:46:13
  • hay objetos mutables que se hashable o objetos inmutables que no son hashable?

En Python, la tupla es inmutable, pero solo es hashable si todos sus elementos son hashables.

>>> tt = (1, 2, (30, 40))
>>> hash(tt)
8027212646858338501
>>> tl = (1, 2, [30, 40])
>>> hash(tl)
TypeError: unhashable type: 'list'

Tipos hashables

  • Los tipos atómicos inmutables son todos hashables, como str, bytes, tipos numéricos
  • Un conjunto congelado es siempre hashable (sus elementos deben ser hashable por definición)
  • Una tupla es hashable solo si todos sus elementos son hashable
  • Los tipos definidos por el usuario son hashables por defecto porque su valor hash es su id ()
 6
Author: liuyihe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-07 09:24:36

Del Glosario de Python :

Un objeto es hashable si tiene un valor hash que nunca cambia durante su vida útil (necesita un método __hash__()), y se puede comparar con otros objetos (necesita un método __eq__() o __cmp__()). Los objetos hashable que comparan equal deben tener el mismo valor hash.

La hashability hace que un objeto sea utilizable como una clave de diccionario y un miembro de conjunto, porque estas estructuras de datos usan el valor hash internamente.

Todos los de Python los objetos incorporados inmutables son hashables, mientras que ningún contenedor mutable (como listas o diccionarios) lo es. Los objetos que son instancias de clases definidas por el usuario son hashables por defecto; todos comparan desiguales, y su valor hash es su id ().

Los dictos y conjuntos deben usar un hash para una búsqueda eficiente en una tabla hash; los valores hash deben ser inmutables, porque cambiar el hash arruinará las estructuras de datos y hará que el dict o conjunto falle. La forma más fácil de hacer el hash valor inmutable es hacer que todo el objeto sea inmutable, por lo que los dos se mencionan a menudo juntos.

Mientras que ninguno de los objetos mutables incorporados es hashable, es posible hacer un objeto mutable con un valor hash que es no mutable. Es común que solo una parte del objeto represente su identidad, mientras que el resto del objeto contiene propiedades que pueden cambiar libremente. Siempre y cuando el valor hash y las funciones de comparación se basen en la propiedades mutables, y la identidad nunca cambia, usted ha cumplido con los requisitos.

 5
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-09 22:40:28

Hay un implícito incluso si no hay una relación explícita forzada entre inmutable y hashable debido a la interacción entre

  1. Los objetos hashables que comparan equal deben tener el mismo valor hash
  2. Un objeto es hashable si tiene un valor hash que nunca cambia durante su vida útil.

No hay problema aquí a menos que redefina __eq__ para que la clase the objects defina la equivalencia en el valor.

Una vez que haya hecho eso, necesita encontrar un función hash estable que siempre devuelve el mismo valor para objetos que representan el mismo valor (por ejemplo, donde __eq__) devuelve True, y nunca cambia durante la vida útil de un objeto.

Es difícil ver una aplicación donde esto es posible, considere una posible clase A que cumpla con estos requisitos. Aunque existe el caso degenerado obvio donde __hash__ devuelve una constante.

Ahora: -

>>> a = A(1)
>>> b = A(1)
>>> c = A(2)
>>> a == b
True
>>> a == c
False
>>> hash(a) == hash(b)
True
>>> a.set_value(c)
>>> a == c
True
>>> assert(hash(a) == hash(c)) # Because a == c => hash(a) == hash(c)
>>> assert(hash(a) == hash(b)) # Because hash(a) and hash(b) have compared equal 
                                 before and the result must stay static over the objects lifetime.

De hecho esto significa en la creación hash (b) = = hash(c), a pesar de el hecho de que nunca se comparan iguales. Me cuesta ver de todos modos definir útilmente __hash__() para un objeto mutable que define compare por valor.

Nota: __lt__, __le__ , __gt__ y las comparaciones __ge__ no se ven afectadas, por lo que aún puede definir un orden de objetos hashables, mutables o de otro modo basados en su valor.

 4
Author: rgammans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-05 09:35:36

Solo porque este es el principal éxito de Google, aquí hay una manera simple de hacer un objeto mutable hashable:

>>> class HashableList(list):
...  instancenumber = 0  # class variable
...  def __init__(self, initial = []):
...   super(HashableList, self).__init__(initial)
...   self.hashvalue = HashableList.instancenumber
...   HashableList.instancenumber += 1
...  def __hash__(self):
...   return self.hashvalue
... 
>>> l = [1,2,3]
>>> m = HashableList(l)
>>> n = HashableList([1,2,3])
>>> m == n
True
>>> a={m:1, n:2}
>>> a[l] = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> m.hashvalue, n.hashvalue
(0, 1)

En realidad encontré un uso para algo como esto al crear una clase para convertir registros SQLAlchemy en algo mutable y más útil para mí, mientras mantenían su hashability para usar como claves dict.

 3
Author: jcomeau_ictx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-23 00:28:06

Inmutable significa que el objeto no cambiará de ninguna manera significativa durante su vida útil. Es una idea vaga pero común en los lenguajes de programación.

La hashabilidad es ligeramente diferente, y se refiere a la comparación.

Hashable Un objeto es hashable si tiene un valor hash que nunca cambios durante su vida útil (necesita un método __hash__()), y puede ser comparado con otros objetos (necesita un método __eq__() o __cmp__()). Objetos hashable que comparan equal debe tener el mismo valor hash.

Todas las clases definidas por el usuario tienen __hash__ método, que por defecto solo devuelve el ID del objeto. Por lo tanto, un objeto que cumple con los criterios de hashability no es necesariamente inmutable.

Los objetos de cualquier clase nueva que declare se pueden usar como clave de diccionario, a menos que lo impida, por ejemplo, lanzando desde __hash__

Podríamos decir que todos los objetos inmutables son hashables, porque si el hash cambia durante el vida, entonces significa que el objeto mutó.

Pero no del todo. Considere una tupla que tiene una lista (mutable). Algunos dicen que la tupla es inmutable, pero al mismo tiempo no es hashable (lanza).

d = dict()
d[ (0,0) ] = 1    #perfectly fine
d[ (0,[0]) ] = 1  #throws

La hashabilidad y la inmutabilidad se refieren a la instancia del objeto, no al tipo. Por ejemplo, un objeto de tipo tupla puede ser hashable o no.

 3
Author: user2622016,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-15 08:36:13

En Python son en su mayoría intercambiables; ya que el hash se supone que representa el contenido, por lo que es tan mutable como el objeto, y tener un objeto cambiar el valor del hash lo haría inutilizable como una clave dict.

En otros idiomas, el valor hash está más relacionado con la 'identidad' de los objetos, y no (necesariamente) con el valor. Por lo tanto, para un objeto mutable, el puntero podría usarse para iniciar el hash. Suponiendo, por supuesto, que un objeto no se mueve en la memoria (como algunos GC hacer). Este es el enfoque utilizado en Lua, por ejemplo. Esto hace que un objeto mutable se pueda usar como una clave de tabla; pero crea varias sorpresas (desagradables) para los novatos.

Al final, tener un tipo de secuencia inmutable (tuplas) lo hace más agradable para las 'claves de múltiples valores'.

 1
Author: Javier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-19 22:58:14

Hashable significa que el valor de una variable puede ser representado (o, mejor dicho, codificado) por una constante string cadena, número, etc. Ahora, algo que está sujeto a cambio (mutable) no puede ser representado por algo que no lo es. Por lo tanto, cualquier variable que es mutable no puede ser hashable y, por la misma razón, solo las variables inmutables pueden ser hashable.

Espero que esto ayude ...

 0
Author: ktdrv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-19 22:43:14