2009-12-24 10 views

Respuesta

37

Respuesta corta: porque son mutables contenedores.

Si un dict fue hash, su hash cambiaría a medida que cambiara su contenido.

+2

Ni siquiera quiero empezar a imaginar la lógica de pesadilla que tomaría usar un objeto como clave. – David

+7

En realidad Python hace que los objetos hash sean fáciles, le da a cada uno una identidad única y constante que puede ser hash. –

+0

sí, pero por defecto las instancias de objetos definidos por el usuario siempre comparan desigualdad. – hop

1

Ninguno de los tipos de contenedores intercambiables en Python es hashable, porque son mutables y, por lo tanto, su valor hash puede cambiar a lo largo de su vida útil.

+4

las tuplas y las cuerdas son contenedores, pero son lavables e inmutables. –

+1

un error estúpido que fácilmente podría haber corregido – hop

+1

@Ignacio Vazquez-Abrams: las cadenas son secuencias pero no contenedores. Además, una tupla que contiene un elemento no lavable tampoco es lavable; intente usar '([1], {2: 3})' como una tecla dict. – tzot

6

Como han dicho otros, el valor hash de un dict cambia a medida que cambia el contenido.

Sin embargo, si realmente necesita usar dicts como claves, puede crear subclases para crear una versión compatible.

>>> class hashabledict(dict): 
... def __hash__(self): 
...  return id(self) 
... 
>>> hd = hashabledict() 
>>> d = dict() 
>>> d[hd] = "foo" 
>>> d 
{{}: 'foo'} 

>>> hd["hello"] = "world" 
>>> d 
{{'hello': 'world'}: 'foo'} 

Esto reemplaza el valor hash utilizado para el dict con la dirección del objeto en la memoria.

+0

y luego puedo reemplazar el byte de dict normal dict = hashabledict – shahjapan

+7

Pero esto es inútil: si almaceno un valor bajo '{}', no puedo buscarlo con '{}', porque los dos hashabledicts vacíos tienen diferentes IDS, y hash diferentes. Lo importante de una función hash es que debe devolver el mismo hash para dos valores "iguales". –

+1

@Ned - Doh! Tienes razón. Lo que realmente se necesita es un frozendict que actúe de la misma manera que un frozenset. Podría crear subclases para definir uno, como en esta receta en ASPN: http://code.activestate.com/recipes/414283/ –

13

Esto es fácil de tratar. Envuelve un dict en un frozenset antes de hash it. Luego, cuando necesite usarlo, conviértalo nuevamente a un dict.

>>> unhashable = {'b': 'a', 'a': 'b'} 
>>> hashable = frozenset(unhashable.items()) 
>>> unhashable = dict(hashable) 
>>> unhashable 
{'a': 'b', 'b': 'a'} 

Tenga en cuenta que el orden de las teclas del diccionario no está definido de todos modos, por lo que el cambio en el orden de las teclas no es importante.

0

Quizás las razones equivocadas me he encontrado con este problema un montón de veces; donde quiero hacer referencia a un dict completo como clave para algo. No es necesario que sea mutable, pero sí quiero preservar y acceder fácilmente a los miembros de dict.

La manera más fácil que he encontrado para hacer que el dict sea inmutable y se pueda utilizar rápidamente como valor clave es convertirlo en un JSON (o serializarlo en su alternativa favorita).

Por ejemplo:

>>> import json 
>>> d = {'hey':1, 'there':2} 
>>> d_key = json.dumps(d) 
>>> d_key 
'{"there": 2, "hey": 1}' 
>>> d2 = {d_key: 'crazytown'} 
>>> d2 
{'{"there": 2, "hey": 1}': 'crazytown'} 

Es fácil de manipular, ya que es sólo una cadena. Y, se puede deserializar en un objeto si desea hacer referencia a sus miembros.

+0

Esto fallará si el diccionario se serializa en un orden diferente. En dicts, el orden de los elementos no importa, pero cuando se crea una cadena a partir de él y se lo compara, es posible que dos dicts con contenido idéntico no coincidan. –

Cuestiones relacionadas