Comment fournir de l'initialisation supplémentaires pour une sous-classe de namedtuple?
Supposons que j'ai un namedtuple
comme ceci:
EdgeBase = namedtuple("EdgeBase", "left, right")
Je veux mettre en œuvre une coutume fonction de hachage pour cela, j'ai donc créer le sous-classe:
class Edge(EdgeBase):
def __hash__(self):
return hash(self.left) * hash(self.right)
Puisque l'objet est immuable, je veux que le hachage de la valeur calculée qu'une seule fois, donc ce que je fais:
class Edge(EdgeBase):
def __init__(self, left, right):
self._hash = hash(self.left) * hash(self.right)
def __hash__(self):
return self._hash
Cela semble fonctionner, mais je suis vraiment pas sûr de sur-classement et l'initialisation en Python, en particulier avec des n-uplets. Existe-il des inconvénients de cette solution? Est-il recommandé de comment faire cela? Est-il bien? Merci à l'avance.
- Des Tests ont montré que ce n'est pas la peine de cache de la valeur de hachage, voir problème n ° 9685
- Note: La manière canonique de combiner les hachages de calculer le hash d'un
tuple
des valeurs combinées;tuple
utilise un plus testé, de façon fiable combinant les hachages de plus naïve des solutions (nettement mieux que la simple multiplication des hachages). Donc, si vous venez de ne pas surcharger de hachage,tuple
's de hachage par défaut, ce serait déjà correcte et efficace (plus efficace que tout ce que vous allez écrire, car il n'exécute aucune Python niveau de byte code). Même si ce n'était pas unnamedtuple
, la bonne solution seraithash((self.left, self.right))
. - bon point. C'était il y a si longtemps, mais si je me souviens bien, la fonction de hachage a été choisi comme ça parce que la classe représentée non orienté bords, de sorte que dans ce cas, il le
tuple
approche ne fonctionne pas. - Dans ce cas, le
hash
d'unfrozenset
des valeurs de travail. Pas aussi bien à la production unique de hachages commetuple
's de hachage (frozenset
juste mélange jusqu'les bits de façon prévisible, alors xors ensemble), mais bien sûr, dans ce cas, c'est le but.
Vous devez vous connecter pour publier un commentaire.
modifier pour 2017: s'avère
namedtuple
n'est pas une bonne idée. attrs est l'alternative moderne.__new__
est ce que vous voulez l'appeler ici parce que les tuples sont immuables. Immuable les objets sont créés dans__new__
et est ensuite retourné à l'utilisateur, au lieu d'être rempli avec des données dans__init__
.cls
doit être passé deux fois à lasuper
appel sur__new__
parce que__new__
est, pour les historiques/impair raisons implicitement unstaticmethod
.super
, et donc une pause dans toute sorte d'héritage multiple situation. Il n'a pas d'importance si vous ne pas utiliser de MI; si quelqu'un d'autre, leur code de briser horriblement. Il n'est pas difficile d'éviter ce problème en utilisant simplementsuper
partout.__slots__ = ()
à la classe. Sinon__dict__
sera créé, la négation de la mémoire de l'efficacité denamedtuple
.__slots__
de casser la cession de _hash et clairement la classe n'est pas en lecture seule car cela permettrait de briser l'affectation à _hash peu importe si c'est fait dans__new__
.namedtuple
, la classe dérivée permet en fait à des paramètres inconnus à être fixé sur l'instance, qui peut ne pas être ce que vous voulez.attrs
ne peut pas être considéré comme "la" alternative moderne à moins que et jusqu'à ce qu'il remplacenamedtuple
dans la bibliothèque standard. Pas besoin d'tiers des dépendances est encore une commune de la contrainte de conception.requests
.Le code dans la question pourrait bénéficier d'un super appel dans le
__init__
dans le cas où il n'est jamais sous-classé dans un héritage multiple situation, mais le contraire est correcte.Tandis que les n-uplets sont en lecture seule uniquement le n-uplet des parties de leurs sous-classes sont en lecture seule, d'autres propriétés peuvent être écrit comme à l'habitude qui est ce qui permet à l'affectation à _hash peu importe si c'est fait dans
__init__
ou__new__
. Vous pouvez faire de la sous-classe entièrement readonly en définissant c'est__slots__
à (), qui a l'avantage d'économiser la mémoire, mais alors vous ne serait pas en mesure d'attribuer à _hash.En Python de 3,7+, vous pouvez maintenant utiliser dataclasses de construire hashable classes avec facilité.
Code
En supposant
int
types deleft
etright
, nous utilisons la valeur par défaut de hachage viaunsafe_hash
+ mot clé:Maintenant, nous pouvons utiliser ces (mutable) hashable objets comme éléments d'un ensemble ou (touches dans un dict).
Détails
On peut aussi remplacer le
__hash__
fonction:Expansion sur @ShadowRanger du commentaire, l'OP personnalisée en fonction de hachage n'est pas fiable. En particulier, les valeurs d'attribut peuvent être échangés, par exemple
hash(Edge(1, 2)) == hash(Edge(2, 1))
, qui n'est probablement pas ce qui est prévu.+Remarque, le nom de "dangereux", suggère le hachage par défaut sera utilisée malgré objet mutable. Cela peut être indésirable, en particulier dans les dict attend immuable clés. Immuable de hachage peut être activée avec les mots clés appropriés. Voir aussi plus sur le hachage de la logique dans dataclasses et un lié à la question de.