John Millikin a proposé une solution similaire à celle-ci:
class A(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, self._b, self._c))
Le problème avec cette solution est que le fichier hash(A(a, b, c)) == hash((a, b, c))
. En d'autres termes, le hachage entre en collision avec celui du tuple de ses membres clés. Peut-être que cela n'a pas d'importance très souvent dans la pratique?
Mise à jour: la documentation Python recommande désormais d'utiliser un tuple comme dans l'exemple ci-dessus. Notez que la documentation indique
La seule propriété requise est que les objets qui se comparent égaux ont la même valeur de hachage
Notez que l'inverse n'est pas vrai. Les objets qui ne sont pas comparables peuvent avoir la même valeur de hachage. Une telle collision de hachage n'entraînera pas le remplacement d'un objet par un autre lorsqu'il est utilisé comme clé de dict ou élément d'ensemble tant que les objets ne se comparent pas également .
Solution obsolète / mauvaise
La documentation Python sur__hash__
suggère de combiner les hachages des sous-composants en utilisant quelque chose comme XOR , ce qui nous donne ceci:
class B(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
if isinstance(othr, type(self)):
return ((self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
return NotImplemented
def __hash__(self):
return (hash(self._a) ^ hash(self._b) ^ hash(self._c) ^
hash((self._a, self._b, self._c)))
Mise à jour: comme le souligne Blckknght, changer l'ordre de a, b et c pourrait poser des problèmes. J'ai ajouté un élément supplémentaire ^ hash((self._a, self._b, self._c))
pour capturer l'ordre des valeurs hachées. Cette finale ^ hash(...)
peut être supprimée si les valeurs combinées ne peuvent pas être réorganisées (par exemple, si elles ont des types différents et par conséquent, la valeur de _a
ne sera jamais attribuée à _b
ou _c
, etc.).
__key
fonction, c'est à peu près aussi rapide que n'importe quel hachage peut l'être. Bien sûr, si les attributs sont connus pour être des entiers, et qu'il n'y en a pas trop, je suppose que vous pourriez potentiellement courir un peu plus vite avec un hachage personnalisé, mais il ne serait probablement pas aussi bien distribué.hash((self.attr_a, self.attr_b, self.attr_c))
va être étonnamment rapide (et correcte ), car la création de petitstuple
s est spécialement optimisée, et elle pousse le travail d'obtention et de combinaison de hachages vers les fonctions intégrées C, ce qui est généralement plus rapide que le code de niveau Python.