Tipos que definen `__eq__ " son unhashable?
-
05-07-2019 - |
Pregunta
Yo tenía un extraño error al trasladar una función de Python 3.1 de la horquilla de mi programa.Yo se redujo a la siguiente hipótesis:
En contraste con Python 2.x, en Python 3.x si un objeto tiene una __eq__
el método es automáticamente unhashable.
¿Es esto cierto?
Esto es lo que sucede en Python 3.1:
>>> class O(object):
... def __eq__(self, other):
... return 'whatever'
...
>>> o = O()
>>> d = {o: 0}
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
d = {o: 0}
TypeError: unhashable type: 'O'
La pregunta es, ¿cómo puedo resolver mi problema personal?Tengo un objeto ChangeTracker
que almacena un WeakKeyDictionary
que apunta a varios objetos, indicando para cada uno el valor de sus pepinillos volcado en un cierto punto del tiempo en el pasado.Siempre que un objeto existente es comprobar en el rastreador de cambio, dice que la nueva solución de limpieza química es idéntica a la de su antiguo y, por lo tanto decir si el objeto ha cambiado en el ínterin.El problema es que ahora ni siquiera puedo comprobar si el objeto dado en la biblioteca, debido a que hace elevar una excepción sobre el objeto que se unhashable.(Causa tiene un __eq__
método). ¿Cómo puedo evitar esto?
Solución
Sí, si define __eq__
, el valor predeterminado __hash__
(es decir, cifrar la dirección del objeto en la memoria) desaparece. Esto es importante porque el hash debe ser coherente con la igualdad: los objetos iguales deben ser el mismo.
La solución es simple: simplemente defina <=> junto con la definición de <=>.
Otros consejos
Este párrafo de http://docs.python.org/3.1 /reference/datamodel.html#object.hash
Si una clase que anula
__eq__()
necesita retener la implementación de__hash__()
de una clase principal, se debe decir al intérprete esto explícitamente configurando__hash__ = <ParentClass>.__hash__
. De lo contrario el la herencia de__hash__
será bloqueado, como si <=> hubiera sido establecido explícitamente en Ninguno.
Compruebe el Python 3 manual de object.__hash__
:
Si una clase no define un
__eq__()
el método no debe definir un__hash__()
operación; si se define__eq__()
pero no__hash__()
, sus instancias no podrán ser utilizados como elementos de hashable colecciones.
El énfasis es mío.
Si quieres ser perezoso, parece que sólo puede definir __hash__(self)
para volver id(self)
:
Definido por el usuario de clases
__eq__()
y__hash__()
los métodos por defecto;con ellos, todos los objetos comparar desigual (con la excepción de ellos mismos) yx.__hash__()
devuelveid(x)
.
No soy un experto en python, pero ¿no tendría sentido que, cuando defina un método eq, también tenga que definir un método hash (que calcula el valor hash para un objeto) De lo contrario, el mecanismo de hash no sabría si golpeó el mismo objeto o un objeto diferente con el mismo valor de hash. En realidad, es al revés, probablemente terminaría calculando diferentes valores hash para objetos considerados iguales por su __eq__
método.
No tengo idea de cómo se llama esa función hash, ¿__hash__
quizás? :)