2011-02-09 15 views
9

Estoy tratando de usar un objeto como clave en un diccionario de Python, pero se está comportando de una manera que no puedo entender del todo.Objetos como claves en los diccionarios de Python

Primero crear un diccionario con mi objeto como la clave:

package_disseminators = { 
    ContentType("application", "zip", "http://other/property") : "one", 
    ContentType("application", "zip") : "two" 
} 

Ahora crear otro objeto que es "el mismo" como uno que es una clave.

content_type = ContentType("application", "zip", "http://other/property") 

he dado el objeto ContentType encargo __eq__ y la costumbre __str__ métodos, tales que el método __eq__ compara el __str__ valores.

Ahora, algunos pitón interactivo:

>>> for key in package_disseminators: 
...  if key == content_type: 
...    print "match" 
...  else: 
...    print "no match" 
... 
no match 
match 

>>> content_type in package_disseminators.keys() 
True 

Ok, por lo que se parece a mi objeto sin duda está siendo identificado correctamente como una llave, por lo que:

>>> package_disseminators[content_type] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
KeyError: (& (type="application/zip") (packaging="http://other/property")) 

Er ... ok? Así que content_type está en la lista package_disseminators.keys(), pero no es una clave?

>>> package_disseminators.has_key(content_type) 
False 

Aparentemente no.

Supongo que el proceso de comparación que Python usa para determinar la igualdad difiere entre una declaración "in" directa en una lista y buscar una clave en un dict, pero no sé cómo. ¿Algún consejo o idea?

Respuesta

17

A partir de la documentación de Python:

claves de un diccionario son casi valores arbitrarios. Valores que no son hashable, es decir, valores que contienen listas , diccionarios u otros tipos mutables (que se comparan por el valor en lugar de por identidad de objeto) no se pueden usar como claves.

Hashable se define como sigue

Un objeto es hashable si tiene un valor hash que nunca cambia durante su vida (que necesita un método __hash__() ), y puede ser comparado con otro objetos (necesita un __eq__() o __cmp__() método). Los objetos modificables que se comparan por igual deben tener el mismo valor hash .

Hashability hace que un objeto utilizable como una clave de diccionario y un miembro del conjunto, debido a que estas estructuras de datos utilizan el valor de hash internamente.

Así que si usted quiere hacer esto, es necesario reemplazar el método por defecto __hash__() en su objeto (ver el comentario de Steven Rumbalski abajo para una explicación más detallada).


>>> content_type in package_disseminators.keys() 
True 

supongo que esto funciona porque dict.keys() devuelve una lista, y probablemente __contains__ cheques por la igualdad, pero no por las mismas hashes.

+6

Algunas aclaraciones adicionales: Su objeto ya tiene un método '__hash__' heredado de' object'. Pero la implementación predeterminada arroja un valor único para cada instancia, por lo que dos instancias iguales tendrán hashes diferentes a menos que proporcione una mejor implementación. 'has_key' compara valores de hash,' in' verifica la igualdad, por eso 'has_key' falla mientras que' in' tiene éxito en tus ejemplos. –

+0

Hola gente. Genial, gracias por esto, muy apreciado! –

11

Como los dicts son tablas hash debajo del capó, necesita definir tanto __eq__ como para que funcionen.

La regla básica es:

  • Para los objetos que __eq__ compara iguales, __hash__ debe devolver el mismo hash.

Desde su descripción, algo así como

def __hash__(self): 
    return hash(str(self)) 

debería funcionar.

+0

¡Gracias por la implementación de '__hash__'! Lamentablemente, no puedo asignar dos respuestas correctas a una pregunta, aunque la combinación de la tuya y la del otro es todo lo que necesito. Cheers, R. –

Cuestiones relacionadas