John Millikin propuso una solución similar a esto:
class A(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
return ((self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
def __hash__(self):
return hash((self._a, self._b, self._c))
El problema de esta solución es que el hash(A(a, b, c)) == hash((a, b, c))
. En otras palabras, el hash colisiona con el de la tupla de sus miembros clave. Tal vez esto no importa muy a menudo en la práctica?
El Python documentation on __hash__
sugiere combinar los hashes de los subcomponentes utilizando algo así como XOR, lo que nos da esto:
class B(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
return (isinstance(othr, type(self))
and (self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
def __hash__(self):
return (hash(self._a)^hash(self._b)^hash(self._c)^
hash((self._a, self._b, self._c)))
Bono: más robusto __eq__
arrojado allí por si acaso.
Actualización: como señala Blckknght, cambiar el orden de a, byc podría causar problemas. Agregué un ^ hash((self._a, self._b, self._c))
adicional para capturar el orden de los valores que se están procesando. Este ^ hash(...)
final puede eliminarse si los valores que se combinan no se pueden reorganizar (por ejemplo, si tienen tipos diferentes y, por lo tanto, el valor de _a
nunca se asignará a _b
o _c
, etc.).
Hm, no pensé en eso. Sin embargo, esto podría generar enormes tuplas/claves cuando el número de atributos que hacen que mi objeto sea único es alto. – user229898
Sí; si su objeto es muy grande, entonces su clave será correspondientemente grande (y el hash caro de calcular). Si los atributos se pueden enumerar (por ejemplo, columnas en un objeto ORM), entonces puede simplificar '__key()'; Sin embargo, igual tendrá que actualizar cada valor de atributo. Realmente no hay forma de evitar esto. –
Esto daría como resultado 'AttributeError' cuando se compara una instancia de' A' con una instancia de la mayoría de las otras clases, incluyendo 'None'. Y puede dar como resultado un falso 'True' si la otra clase tiene los mismos atributos. ¿No sería eso un problema en la mayoría de los casos? Si es así, ¿deberíamos verificar manualmente que sea la misma clase? – max