TL; DR:
Fare riferimento al glossario : hash()
viene utilizzato come scorciatoia per confrontare oggetti, un oggetto è considerato hash se può essere confrontato con altri oggetti. ecco perché usiamo hash()
. E 'anche usato per l'accesso dict
e set
gli elementi che vengono implementati come tabelle hash ridimensionabili in CPython .
Considerazioni tecniche
- di solito il confronto di oggetti (che può coinvolgere diversi livelli di ricorsione) è costoso.
- preferibilmente, la
hash()
funzione è un ordine di grandezza (o più) meno costosa.
- confrontare due hash è più facile che confrontare due oggetti, è qui che si trova il collegamento.
Se leggi come sono implementati i dizionari , usano tabelle hash, il che significa che derivare una chiave da un oggetto è una pietra angolare per il recupero di oggetti nei dizionari in formato O(1)
. Tuttavia, dipende molto dalla tua funzione hash per essere resistente alle collisioni . Il caso peggiore per ottenere un elemento in un dizionario è in realtà O(n)
.
In questa nota, gli oggetti modificabili di solito non sono modificabili. La proprietà hashable significa che puoi usare un oggetto come chiave. Se il valore hash viene utilizzato come chiave e il contenuto dello stesso oggetto cambia, cosa dovrebbe restituire la funzione hash? È la stessa chiave o un'altra? Esso dipende da come si definisce la funzione di hash.
Imparare con l'esempio:
Immagina di avere questa classe:
>>> class Person(object):
... def __init__(self, name, ssn, address):
... self.name = name
... self.ssn = ssn
... self.address = address
... def __hash__(self):
... return hash(self.ssn)
... def __eq__(self, other):
... return self.ssn == other.ssn
...
Nota: tutto questo si basa sul presupposto che il SSN non cambia mai per un individuo (non so nemmeno dove verificare effettivamente quel fatto da fonte autorevole).
E abbiamo Bob:
>>> bob = Person('bob', '1111-222-333', None)
Bob va a vedere un giudice per cambiare il suo nome:
>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')
Questo è quello che sappiamo:
>>> bob == jim
True
Ma questi sono due oggetti diversi con una memoria allocata diversa, proprio come due record diversi della stessa persona:
>>> bob is jim
False
Ora arriva la parte in cui hash () è utile:
>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
Indovina un po:
>>> dmv_appointments[jim]
'tomorrow'
Da due record differenti è possibile accedere alle stesse informazioni. Ora prova questo:
>>> dmv_appointments[hash(jim)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in __eq__
AttributeError: 'int' object has no attribute 'ssn'
>>> hash(jim) == hash(hash(jim))
True
Cosa è appena successo? È una collisione. Poiché hash(jim) == hash(hash(jim))
sono entrambi numeri interi, dobbiamo confrontare l'input di __getitem__
con tutti gli elementi che entrano in collisione. Il builtin int
non ha un ssn
attributo quindi scatta.
>>> del Person.__eq__
>>> dmv_appointments[bob]
'tomorrow'
>>> dmv_appointments[jim]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: <__main__.Person object at 0x7f611bd37110>
In questo ultimo esempio, mostro che anche con una collisione, il confronto viene eseguito, gli oggetti non sono più uguali, il che significa che solleva con successo a KeyError
.