Les types qui définissent `__eq__` ne sont pas fastidieux?
-
05-07-2019 - |
Question
J'avais un étrange bug lors du portage d'une fonctionnalité sur la fourche Python 3.1 de mon programme. Je l'ai réduite à l'hypothèse suivante:
Contrairement à Python 2.x, dans Python 3.x, si un objet utilise une méthode __eq__
, il est automatiquement soulagé.
Est-ce vrai?
Voici ce qui se passe dans 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 question de suivi est la suivante: comment puis-je résoudre mon problème personnel? J'ai un objet ChangeTracker
qui stocke un WeakKeyDictionary
pointant vers plusieurs objets, donnant pour chacun la valeur de leur vidage de cornichons à un moment donné dans le passé. Chaque fois qu'un objet existant est archivé, l'outil de suivi des modifications indique si son nouveau pickle est identique à l'ancien, indiquant donc si l'objet a changé entre-temps. Le problème, c’est que maintenant je ne peux même pas vérifier si l’objet donné se trouve dans la bibliothèque, car cela le fait lever une exception à propos de la pureté de l’objet. (Parce que sa méthode est <=>.) Comment puis-je contourner ce problème?
La solution
Oui, si vous définissez __eq__
, la valeur par défaut __hash__
(à savoir, le hachage de l'adresse de l'objet en mémoire) disparaît. Ceci est important car le hachage doit être cohérent avec l'égalité: les objets égaux doivent avoir le même hachage.
La solution est simple: définissez simplement <=> en même temps que <=>.
Autres conseils
Ce paragraphe de http://docs.python.org/3.1 /reference/datamodel.html#object. hash
Si une classe qui annule
__eq__()
doit conserver la mise en œuvre de__hash__()
d'une classe parente, l'interprète doit être informé de cette explicitement en définissant__hash__ = <ParentClass>.__hash__
. Sinon le l'héritage de__hash__
sera bloqué, comme si <=> avait été explicitement défini sur Aucun.
Consultez le manuel de Python 3 sur object.__hash__
:
Si une classe ne définit pas de méthode
__eq__()
, elle ne devrait pas non plus définir une opération__hash__()
; s'il définit__hash__(self)
mais pasid(self)
, ses instances ne seront pas utilisables en tant qu'éléments de collections à caractère amovible.
L’accent est à moi.
Si vous voulez être paresseux, il semble que vous puissiez simplement définir x.__hash__()
pour renvoyer id(x)
:
Les classes définies par l'utilisateur ont <=> et <=> méthodes par défaut; avec eux, tous les objets comparent de manière inégale (sauf avec eux-mêmes) et <=> renvoie <=>.
Je ne suis pas un expert en python, mais cela n’aurait-il pas un sens que, lorsque vous définissez une méthode eq, vous devez également définir une méthode de hachage (qui calcule la valeur de hachage d'un objet), sinon, le mécanisme de hachage ne sait pas s'il frappe le même objet ou un objet différent avec la même valeur de hachage. En fait, c’est l’inverse, il faudrait probablement calculer différentes valeurs de hachage pour les objets considérés égaux par votre __eq__
méthode.
Je n'ai aucune idée du nom de cette fonction de hachage, __hash__
peut-être? :)