John Millikin ha proposto una soluzione simile a questo:
class A(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
return ((self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
def __hash__(self):
return hash((self._a, self._b, self._c))
Il problema di questa soluzione è che il hash(A(a, b, c)) == hash((a, b, c))
. In altre parole, l'hash si scontra con quello della tupla dei suoi membri chiave. Forse questo non importa molto spesso nella pratica?
La Python documentation on __hash__
suggerisce di combinare gli hash dei sub-componenti utilizzando qualcosa come XOR, che ci dà questa:
class B(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
return (isinstance(othr, type(self))
and (self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
def __hash__(self):
return (hash(self._a)^hash(self._b)^hash(self._c)^
hash((self._a, self._b, self._c)))
Bonus: più robusta __eq__
gettato lì dentro per buona misura.
Aggiornamento: come sottolinea Blckknght, la modifica dell'ordine di a, b, e c potrebbe causare problemi. Ho aggiunto un ulteriore ^ hash((self._a, self._b, self._c))
per acquisire l'ordine dei valori sottoposti a hash. Questa finale ^ hash(...)
possono essere rimossi se i valori essendo associati non possono essere riorganizzati (per esempio, se hanno diversi tipi e quindi il valore di _a
non verrà mai assegnato a _b
o _c
, ecc).
Hm, non ci ho pensato. Tuttavia, questo potrebbe portare a enormi tuple/chiavi quando il numero di attributi che rendono unico il mio oggetto è alto. – user229898
Sì; se il tuo oggetto è molto grande, la sua chiave sarà corrispondentemente grande (e l'hash è costoso da calcolare). Se gli attributi possono essere enumerati (ad es., Le colonne in un oggetto ORM), allora puoi semplificare '__key()'; tuttavia, dovrai comunque eseguire il hashing di ogni valore di attributo. Non c'è davvero alcun modo per aggirare questo. –
Ciò determinerebbe "AttributeError" quando si confronta un'istanza di "A" con un'istanza della maggior parte delle altre classi, incluso "Nessuno". E può risultare in un falso 'Vero' se l'altra classe ha gli stessi attributi. Non sarebbe un problema nella maggior parte dei casi? In tal caso, dovremmo controllare manualmente che si tratti della stessa classe? – max