TL; DR:
Consulte o glossário : hash()é usado como um atalho para a comparação de objetos, um objeto é considerado hashable se puder ser comparado a outros objetos. é por isso que usamos hash(). Ele também é usado para acessar dicte setelementos que são implementados como tabelas hash redimensionáveis em CPython .
Considerações técnicas
- geralmente comparar objetos (o que pode envolver vários níveis de recursão) é caro.
- de preferência, a
hash()função é uma ordem de magnitude (ou várias) menos cara.
- comparar dois hashes é mais fácil do que comparar dois objetos, é aqui que está o atalho.
Se você ler sobre como os dicionários são implementados , eles usam tabelas hash, o que significa que derivar uma chave de um objeto é uma pedra angular para recuperar objetos em dicionários em O(1). No entanto, isso depende muito da sua função hash para ser resistente a colisões . O pior caso para obter um item em um dicionário é realmente O(n).
Nessa nota, objetos mutáveis geralmente não são hashable. A propriedade hashable significa que você pode usar um objeto como uma chave. Se o valor hash for usado como uma chave e o conteúdo desse mesmo objeto for alterado, o que a função hash deve retornar? É a mesma chave ou diferente? Ele depende de como você define a sua função hash.
Aprendendo pelo exemplo:
Imagine que temos esta aula:
>>> 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
...
Observação: tudo isso se baseia na suposição de que o SSN nunca muda para um indivíduo (nem mesmo sei onde verificar esse fato de uma fonte confiável).
E nós temos Bob:
>>> bob = Person('bob', '1111-222-333', None)
Bob vai ver um juiz para mudar seu nome:
>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')
Isso é o que sabemos:
>>> bob == jim
True
Mas esses são dois objetos diferentes com memória alocada diferente, assim como dois registros diferentes da mesma pessoa:
>>> bob is jim
False
Agora vem a parte em que o hash () é útil:
>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
Adivinha:
>>> dmv_appointments[jim]
'tomorrow'
A partir de dois registros diferentes, você pode acessar as mesmas informações. Agora tente isto:
>>> 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
O que acabou de acontecer? Isso é uma colisão. Porque hash(jim) == hash(hash(jim))ambos são inteiros btw, precisamos comparar a entrada de __getitem__com todos os itens que colidem. O embutido intnão tem um ssnatributo, então ele desarma.
>>> 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>
Neste último exemplo, mostro que mesmo com uma colisão, a comparação é realizada, os objetos não são mais iguais, o que significa que levanta a com sucesso KeyError.