230 votes

Objet de type personnalisé comme clé de dictionnaire

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 de l'objet" serve de clé), par exemple

class MyThing:
    def __init__(self,name,location,length):
            self.name = name
            self.location = location
            self.length = length

Je voudrais utiliser les MyThing comme des clés qui sont considérées comme identiques si le nom et l'emplacement sont les mêmes. Depuis C#/Java, j'ai l'habitude de devoir surcharger et fournir une méthode equals et hashcode, et de promettre de ne pas muter ce dont le hashcode dépend.

Que dois-je faire en Python pour y parvenir ? Devrais-je même le faire ?

(Dans un cas simple, comme ici, il serait peut-être préférable de placer un n-uplet (nom, emplacement) comme clé - mais je pense que je voudrais que la clé soit un objet).

272voto

6502 Points 42700

Vous devez ajouter deux méthodes :

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)

Le Python documentation sur les dictées définit ces exigences sur les objets clés, c'est-à-dire qu'ils doivent être hachable .

38voto

Sven Marnach Points 133943

Une alternative dans Python 2.6 ou supérieur est d'utiliser collections.namedtuple() -- cela vous évite d'avoir à é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

22voto

Skurmedel Points 9227

Vous passez outre __hash__ si vous voulez une sémantique de hachage spéciale, et __cmp__ ou __eq__ afin de rendre votre classe utilisable en tant que clé. Les objets qui se comparent de manière égale doivent avoir la même valeur de hachage.

Python s'attend à ce que __hash__ pour retourner un nombre entier, en retournant Banana() n'est pas recommandé :)

Les classes définies par l'utilisateur ont __hash__ par défaut qui appelle id(self) comme vous l'avez noté.

Il y a quelques conseils supplémentaires de la documentation . :

Les classes qui héritent d'un __hash__() d'une classe parente mais en changeant la signification de __cmp__() ou __eq__() de sorte que la valeur de hachage retournée n'est plus appropriée (par exemple, en passant à un concept d'égalité basé sur la valeur au lieu de l'égalité par défaut l'égalité par défaut basée sur l'identité) peuvent se signaler explicitement comme étant non hachable en définissant __hash__ = None dans la définition de la classe. En procédant ainsi signifie que non seulement les instances de la classe soulèveront une TypeError lorsqu'un programme tente de de récupérer leur valeur de hachage, mais elles seront aussi correctement identifiées comme non hachable lors de la vérification isinstance(obj, collections.Hashable) (contrairement aux classes qui définissent leur propre __hash__() pour lever explicitement TypeError).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X