Eu vi um exemplo de código onde a hash
função é aplicada a uma tupla. Como resultado, ele retorna um número inteiro negativo. Eu me pergunto o que essa função faz? O Google não ajuda. Encontrei uma página que explica como o hash é calculado, mas não explica por que precisamos dessa função.
86
Respostas:
Um hash é um número inteiro de tamanho fixo que identifica um valor específico . Cada valor precisa ter seu próprio hash, portanto, para o mesmo valor, você obterá o mesmo hash, mesmo que não seja o mesmo objeto.
>>> hash("Look at me!") 4343814758193556824 >>> f = "Look at me!" >>> hash(f) 4343814758193556824
Os valores de hash precisam ser criados de forma que os valores resultantes sejam distribuídos uniformemente para reduzir o número de colisões de hash que você obtém. As colisões de hash ocorrem quando dois valores diferentes têm o mesmo hash. Portanto, mudanças relativamente pequenas geralmente resultam em hashes muito diferentes.
>>> hash("Look at me!!") 6941904779894686356
Esses números são muito úteis, pois permitem a consulta rápida de valores em uma grande coleção de valores. Dois exemplos de seu uso são Python's
set
edict
. Em alist
, se você quiser verificar se um valor está na lista, comif x in values:
, Python precisa percorrer toda a lista e compararx
com cada valor na listavalues
. Isso pode levar muito tempolist
. Em aset
, o Python rastreia cada hash e, quando você digitaif x in values:
, o Python obtém o valor do hash parax
, procura-o em uma estrutura interna e depois compara apenasx
com os valores que têm o mesmo hash quex
.A mesma metodologia é usada para pesquisa de dicionário. Isso torna a pesquisa em
set
edict
muito rápida, enquanto a pesquisa emlist
é lenta. Também significa que você pode ter objetos não hashable em alist
, mas não em aset
ou como chaves em adict
. O exemplo típico de objetos sem hash é qualquer objeto mutável, o que significa que você pode alterar seu valor. Se você tiver um objeto mutável, ele não deve ser hash, pois seu hash mudará ao longo de sua vida útil, o que causaria muita confusão, pois um objeto poderia terminar com o valor de hash errado em um dicionário.Observe que o hash de um valor só precisa ser o mesmo para uma execução do Python. No Python 3.3, eles mudarão de fato a cada nova execução do Python:
$ /opt/python33/bin/python3 Python 3.3.2 (default, Jun 17 2013, 17:49:21) [GCC 4.6.3] on linux Type "help", "copyright", "credits" or "license" for more information. >>> hash("foo") 1849024199686380661 >>> $ /opt/python33/bin/python3 Python 3.3.2 (default, Jun 17 2013, 17:49:21) [GCC 4.6.3] on linux Type "help", "copyright", "credits" or "license" for more information. >>> hash("foo") -7416743951976404299
Isso torna mais difícil adivinhar qual valor hash uma determinada string terá, que é um recurso de segurança importante para aplicativos da web, etc.
Os valores de hash, portanto, não devem ser armazenados permanentemente. Se você precisar usar valores de hash de forma permanente, poderá dar uma olhada nos tipos mais "sérios" de hashes, funções de hash criptográficas , que podem ser usados para fazer somas de verificação verificáveis de arquivos etc.
fonte
hash(-1) == hash(-2)
(executando Python 2.7)hash(-1) == hash(-2)
ainda existe hoje. Felizmente, isso não afeta adversamente o dicionário e as pesquisas definidas. Todos os outros inteirosi
resolvem para si mesmos,hash(i)
exceto-1
.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 usamoshash()
. Ele também é usado para acessardict
eset
elementos que são implementados como tabelas hash redimensionáveis em CPython .Considerações técnicas
hash()
função é uma ordem de magnitude (ou várias) menos cara.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 é realmenteO(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 embutidoint
não tem umssn
atributo, 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
.fonte
hash()
é um número inteiro de tamanho fixo, que pode causar colisão__eq__
no exemplo acima. É chamado pelo dicionário quando tenta comparar a chave que recebe com todas as chaves que possui? De tal forma quedel
pelo__eq__
método no último exemplo, o dicionário não tem nada a chamada para usar para determinar a equivalência da chave que recebeu com as chaves que ele tem?hash(jim)
.Person.__eq__
é chamado porque a chave existente tem o mesmo hashhash(jim)
para garantir quePerson.__eq__
seja usada a chave certa . Ele erra porque assume queother
, isto éint
, tem umssn
atributo. Se ahash(jim)
chave não existisse no dicionário__eq__
, não seria chamada. Isso explica quando a pesquisa de chave pode ser O (n): quando todos os itens têm o mesmo hash,__eq__
deve ser usado em todos eles, por exemplo, no caso em que a chave não existe.dmv_appointments[bob.ssn] = 'tomorrow'
, dispensando a necessidade de definir um__hash__
método? Eu entendo que adiciona 4 caracteres para cada compromisso que você escreve e lê, mas parece mais claro para mim.A documentação do
hash()
Python para o estado:Os dicionários Python são implementados como tabelas hash. Portanto, sempre que você usa um dicionário,
hash()
é chamado nas chaves que você passa para a atribuição, ou consulta.Além disso, os documentos para o
dict
estado do tipo :fonte
O hash é usado por dicionários e conjuntos para pesquisar rapidamente o objeto. Um bom ponto de partida é o artigo da Wikipedia sobre tabelas de hash .
fonte
Você pode usar o
Dictionary
tipo de dados em python. É muito semelhante ao hash - e também suporta aninhamento, semelhante ao hash aninhado.Exemplo:
dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'} dict['Age'] = 8; # update existing entry dict['School'] = "DPS School" # Add new entry print ("dict['Age']: ", dict['Age']) print ("dict['School']: ", dict['School'])
Para obter mais informações, consulte este tutorial sobre o tipo de dados do dicionário .
fonte