__hash__ - python hash()
Cómo implementar una buena función__hash__ en python (3)
Documentación para el object.__hash__(self)
La única propiedad requerida es que los objetos que se comparan por igual tengan el mismo valor hash; se aconseja mezclar de alguna manera (por ejemplo, utilizando exclusivamente o) los valores hash para los componentes del objeto que también desempeñan un papel en la comparación de los objetos.
def __hash__(self):
return hash(self.a) ^ hash(self.b)
Al implementar una clase con propiedades múltiples (como en el ejemplo de juguete a continuación), ¿cuál es la mejor manera de manejar hash?
Supongo que __eq__
y __hash__
deberían ser consistentes, pero ¿cómo implementar una función hash adecuada que sea capaz de manejar todas las propiedades?
class AClass:
def __init__(self):
self.a = None
self.b = None
def __eq__(self, other):
return other and self.a == other.a and self.b == other.b
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self.a, self.b))
Leí sobre esta cuestión que las tuplas son lavables, así que me preguntaba si algo como el ejemplo anterior era sensato. ¿Lo es?
Es peligroso escribir
def __eq__(self, other):
return other and self.a == other.a and self.b == other.b
porque si su objeto rhs (es decir, other
) evalúa a boolean False, ¡nunca se comparará con nada!
Además, es posible que desee comprobar si other
pertenece a la clase o subclase de AClass
. Si no lo hace, obtendrá la excepción AttributeError
o un falso positivo (si la otra clase tiene los mismos atributos con valores coincidentes). Así que recomendaría reescribir __eq__
como:
def __eq__(self, other):
return isinstance(other, self.__class__) and self.a == other.a and self.b == other.b
Si, por casualidad, desea una comparación inusualmente flexible, que se compare entre clases no relacionadas, siempre y cuando los atributos coincidan por su nombre, aún desea evitar al menos AttributeError
y verificar que el other
no tenga ningún atributo adicional. Cómo lo hace depende de la situación (ya que no hay una forma estándar de encontrar todos los atributos de un objeto).
__hash__
debería devolver el mismo valor para los objetos que son iguales. Tampoco debería cambiar a lo largo de la vida del objeto; en general, solo lo implementas para objetos inmutables.
Una implementación trivial sería simplemente return 0
. Esto siempre es correcto, pero funciona mal.
Su solución, devolver el hash de una tupla de propiedades, es buena. Pero tenga en cuenta que no necesita enumerar todas las propiedades que compara en __eq__
en la tupla. Si alguna propiedad generalmente tiene el mismo valor para los objetos desiguales, simplemente omítala. No haga que el cálculo de hash sea más caro de lo que debe ser.
Editar: recomendaría no usar xor para mezclar hashes en general. Cuando dos propiedades diferentes tienen el mismo valor, tendrán el mismo hash, y con xor éstas se cancelarán mutuamente. Las tuplas usan un cálculo más complejo para mezclar hashes, ver tuplehash
en tupleobject.c
.