Que dois-je faire pour utiliser mes objets d'un type personnalisé comme clés dans un dictionnaire Python (où je ne veux pas que l '"identifiant d'objet" agisse comme clé), par exemple
class MyThing:
def __init__(self,name,location,length):
self.name = name
self.location = location
self.length = length
Je voudrais utiliser MyThing comme des clés considérées comme identiques si le nom et l'emplacement sont les mêmes. Depuis C # / Java, j'ai l'habitude de devoir remplacer et de fournir une méthode d'égalité et de hashcode, et je promets de ne pas muter quoi que ce soit dont le hashcode dépend.
Que dois-je faire en Python pour accomplir cela? Dois-je même?
(Dans un cas simple, comme ici, il serait peut-être préférable de simplement placer un tuple (nom, emplacement) comme clé - mais considérez que je voudrais que la clé soit un objet)
python
dictionary
Anonyme
la source
la source
MyThing
, s'ils ont le mêmename
etlocation
, pour indexer le dictionnaire pour renvoyer la même valeur, même s'ils ont été créés séparément comme deux "objets" différents.Réponses:
Vous devez ajouter 2 méthodes , notez
__hash__
et__eq__
:class MyThing: def __init__(self,name,location,length): self.name = name self.location = location self.length = length def __hash__(self): return hash((self.name, self.location)) def __eq__(self, other): return (self.name, self.location) == (other.name, other.location) def __ne__(self, other): # Not strictly necessary, but to avoid having both x==y and x!=y # True at the same time return not(self == other)
La documentation Python dict définit ces exigences sur les objets clés, c'est-à-dire qu'ils doivent être hachables .
la source
hash(self.name)
semble plus agréable queself.name.__hash__()
, et si vous le faites et vous pouvez le fairehash((x, y))
pour éviter de vous faire du XOR.x.__hash__()
comme ça est également faux , car cela peut produire des résultats incorrects : pastebin.com/C9fSH7eFand
pour__eq__
mais j'ai pensé "pourquoi ne pas utiliser de tuples?" parce que je fais souvent ça de toute façon (je pense que c'est plus lisible). Pour une raison étrange, mes yeux ne sont pas retournés à la question__hash__
.__ne__()
a été "corrigée" .Une alternative dans Python 2.6 ou supérieur est d'utiliser
collections.namedtuple()
- cela vous évite d'écrire des méthodes spéciales:from collections import namedtuple MyThingBase = namedtuple("MyThingBase", ["name", "location"]) class MyThing(MyThingBase): def __new__(cls, name, location, length): obj = MyThingBase.__new__(cls, name, location) obj.length = length return obj a = MyThing("a", "here", 10) b = MyThing("a", "here", 20) c = MyThing("c", "there", 10) a == b # True hash(a) == hash(b) # True a == c # False
la source
Vous remplacez
__hash__
si vous voulez une sémantique de hachage spéciale, et /__cmp__
ou__eq__
afin de rendre votre classe utilisable comme clé. Les objets qui comparent la même valeur doivent avoir la même valeur de hachage.Python s'attend
__hash__
à renvoyer un entier, le retourBanana()
n'est pas recommandé :)Les classes définies par l'utilisateur ont
__hash__
par défaut qui appelleid(self)
, comme vous l'avez noté.Il y a quelques conseils supplémentaires dans la documentation .:
la source
__eq__
ou__cmp__
.__cmp__
est donné par Python s'il s'agit d'une classe définie par l'utilisateur, mais vous voudrez probablement les remplacer quand même pour tenir compte de la nouvelle sémantique.cmp
et utiliser=
des classes d'utilisateurs qui ne remplacent pas ces méthodes, l'une d'elles doit être implémentée pour répondre à l'exigence du questionneur voulant que les instances avec un nom et un emplacement similaires aient la même clé de dictionnaire.