Bonjour,
Quel est le danger à définir hash via id() pour une classe dérivée de dict ?
S'il n'y en pas, pourquoi la classe dict n'est elle pas hashable ?Code:
1
2 def __hash__(self): return id(self)
Merci d'avance
Version imprimable
Bonjour,
Quel est le danger à définir hash via id() pour une classe dérivée de dict ?
S'il n'y en pas, pourquoi la classe dict n'est elle pas hashable ?Code:
1
2 def __hash__(self): return id(self)
Merci d'avance
Il me semble pourtant qu'une méthode soit adaptée à ce cas non
Citation:
>>> d = {}
>>> dir(d)
['__class__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
J'ai peut-être compris votre question qui est plus proche de l'intérêt de la fonction hash() sur un dictionnaire.
La fonction hash() n'a pas été véritablement prévu pour être utilisé avec les dictionnaires, mais elle permet de déterminer si un objet peut servir comme clé de dictionnaire ou non.
Un objet ne peut être utilisé comme clé de dictionnaire que si l'objet est hachable.
Espérant vous avoir aidé
Edit : On pourrait l'utiliser de cette manière
Une liste n'étant pas hachable, on peut vérifier qu'une liste ne peut être une clé du dictionnaireCode:
1
2
3
4
5
6
7
8
9
10 >>> l_1 = ["a", "b", "c"] >>> d = {} >>> for ind, letter in enumerate(l_1): ... if hash(letter): ... d[letter] = ind ... else: ... raise TypeError ... >>> d {'a': 0, 'c': 2, 'b': 1}
Code:
1
2
3
4 >>> d = {[12, 5]:"a"} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list'
Merci Fred,
Je croyais avoir déjà vu l'erreur "dict is unhashable" en py2.6 (je n'ai pas Python sous la mai, je vérifierai ce soir). Mais la question est la même pour les listes:
Quel danger à utiliser id(self) comme clef de hashage pour une liste ? Pourquoi n'est-ce pas le comportement par défaut ?
Si vous avez suivi mon raisonnement, cela veut dire qu'un dictionnaire ne peut être une clé d'un dictionnaire.Citation:
Je croyais avoir déjà vu l'erreur "dict is unhashable" en py2.6 (je n'ai pas Python sous la mai, je vérifierai ce soir). Mais la question est la même pour les listes:
Code:
1
2
3
4
5 >>> d = {} >>> dic = {d:15, "a":1} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'dict'
Tout simplement parce-que id() retourne une identité, et comme chaque objet (liste, dictionnaire, etc...) en ont une, ce ne sera pas une façon de vérifier que cet objet peut être une clé d'un dictionnaire.Citation:
Quel danger à utiliser id(self) comme clef de hashage pour une liste ? Pourquoi n'est-ce pas le comportement par défaut ?
Bonjour,
Il doit y avoir une raison technique pour laquelle des structures complexes comme une liste ou un dictionnaire ne sont pas hashables.
Mais... quel pourrait être l'intérêt pratique d'utiliser un dictionnaire ou une liste comme clé (donc hashée) d'un dictionnaire?
En fait tout ce qui est "modifiable" ne peut être une clé de dictionnaire.
Les tuples "non modifiables" peuvent être une clé.
Code:
1
2
3
4 >>> tup = (12,) >>> d = {tup:5} >>> d {(12,): 5}
Tant que la clef ne dépend pas de ce qui est modifiable (i.e id()), quel est le problème ?Citation:
tout ce qui est "modifiable" ne peut être une clé de dictionnaire.
J'ai rencontré ce problème en codant une classe avec les méthodes suivantesCitation:
quel pourrait être l'intérêt pratique
*link(self,container) : rajoute self.stock_as() au conteneur, et mémorise le conteneur
*unlink(self) : retire self de tous les conteneurs qui ont été link().
Dans link, afin de mémoriser le conteneur, et ce que j'y ai entré, j'ai tenté un
Pour contourner le problème de non hashable, j'ai finalement opté pourCode:self._linked_to[container]=self.stock_as()
@fred : dsl, je ne comprends pas pourquoi le dictionnaire n'est pas hashable alors qu'il définit __hash__()Code:self._linked_to[id(container)]=(container,self.stock_as())
Ca ne me convainc pas, sachant que les instances sont usuellement hashables en utilisant justement id (donc pour les instances normales, être hashable et avoir une id() est la même chose!)Citation:
Tout simplement parce-que id() retourne une identité, et comme chaque objet (liste, dictionnaire, etc...) en ont une, ce ne sera pas une façon de vérifier que cet objet peut être une clé d'un dictionnaire.
Non, la clé fonctionne avec id() tout simplement parce-que id() renvoie un type int qui lui est hashable.Citation:
Ca ne me convainc pas, sachant que les instances sont usuellement hashables en utilisant justement id (donc pour les instances normales, être hashable et avoir une id() est la même chose!)
La documentation officielle vous persuadera je l'espèreCitation:
@fred : dsl, je ne comprends pas pourquoi le dictionnaire n'est pas hashable alors qu'il définit __hash__()
Citation:
Envoyé par doc officielle
On peut avoir deux dict ou list différentes contenant les mêmes objets.
De ce fait, les containers mutables ne sont pas hash-ables car l'égalité (du contenu) n'implique pas l'identité (du contenant) - pré-requis à __hash__.
- W
Merci Wiztricks,
Est ce que cela implique, pour rendre hashable une dérivée de dict, qu'il ne me suffit pas de surcharger __hash__, mais qu'il faut aussi surcharger l'égalité (de sorte que l'on compare les id et non le contenu) ?
Techniquement, oui, tu pourrais faire cela: il faut, et il suffit, que deux objets "égaux" (au sens de __eq__) aient le même hash, et que ce hash soit constant. Cela implique que des objets qui possèdent des hash différents, même s'il sont mutables, ne pourront jamais devenir "égaux".
J'ai du mal à comprendre ce que ça t'apporterait; tu réduis la notion d'égalité à celle d'identité, mais ta solution est valide.
En pratique, tous les exemples que j'ai vu définissant __hash__ sur des objets mutables étaient toujours foireux...
dividee a déjà répondu à la question.
Les objets "utilisateurs", mutables ou pas, ont par défaut un __hash__ defini à id(self) et un __eq__(self, other) retournant id(self) == id(other).
Il n'est donc pas si hasardeux de définir une fonction de hash partout!
Et d'utiliser n'importe quelle instance "utilisateur" (mutable ou pas) comme clé d'un dict...
Utiliser id comme hash est quelque peu restrictif pour appliquer cela, par défaut, aux dict et aux list. Ca ne serait peut être plus cohérents avec certaines attentes, intuitions.
Votre s/classe de dict contenant des attributs spécifiques, la comparaison de deux instances sera basée sur la comparaison des "dict"s de la surclasse => il faudra redéfinir l'égalité... et pourquoi pas le hash.
De fait, si vous n'aviez pas s/classé dict mais crée une classe ayant un dict comme attribut, vous auriez évité ce problème (pour d'autres).
=> l'autre question est de savoir s'il est "raisonnable" d'avoir une s/classe de dict si au bout du compte on le dépoile de ses attributs. Seul vous pouvez y répondre.
- W
Merci beaucoup à vous tous pour ces précisions !
Oui. c'est également le cas pour toutes les users-classes qui ne surchargent pas l'égalité. Et ca ne choque en général personne de les mettre dans un set()Citation:
tu réduis la notion d'égalité à celle d'identité
- J'ai donné un premier exemple: avoir des instances qui savent à quels conteneurs elles appartiennent.Citation:
J'ai du mal à comprendre ce que ça t'apporterait
- Un autre exemple: un jeu vidéo avec des objets, disons des créatures. J'ai besoin de les stocker dans des set(), mais j'ai également besoin qu'elles héritent de dict pour stocker leurs caractéristiques de jeu (force endurance etc).
Dans ce second exemple, chaque instance de créature est différente des autres, donc réduire l'égalité à l'identité me semble justifié.
Dans mon cas (2eme exemple ci dessus), j'ai besoin de 2 types d'accès aux attributs d'une instance:Citation:
s'il est "raisonnable" d'avoir une s/classe de dict si au bout du compte on le dépoile de ses attributs
-creature.attribut me sert a stocker des attributs locaux (non synchronisés par l'envoi de messages réseaux)
- creature['force']=1 envoie un message réseau pour synchroniser la valeur. Comme wiztricks le suggère, j'aurais tout aussi bien pu faire creature.synchro_data.force=1, mais c'est plus lourd à écrire...
Encore merci à vous