DEV Community 👩‍💻👨‍💻

Ítalo Epifânio
Ítalo Epifânio

Posted on • Updated on

Objetos hashable em Python

Hash de objetos é uma representação númerica inteira que é obtida utilizando o dunder method __hash__. Compreender esse conceito ajuda a entender como as estruturas de dados Python funcionam, uma vez que o hash dos objetos são utilizados internamente pela linguagem.

Programadores Python costumam se deparar com o conceito de hash de objetos quando tentam armazenar um objeto sem hash em uma estrutura de dados da linguagem. Por exemplo:

from dataclasses import dataclass

@dataclass
class Pessoa:
    nome: str
    cpf: str

p = Pessoa(nome="Ítalo Epifânio", cpf="1010101010")
pessoas = set()
pessoas.add(p)
Enter fullscreen mode Exit fullscreen mode

O código acima define uma classe pessoa e um objeto p do tipo pessoa. Ao tentar adicionar uma pessoa ao conjunto pessoas o seguinte erro é lançado:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'Pessoa'
Enter fullscreen mode Exit fullscreen mode

Isso acontece porque a estrutura de dados "conjunto" do Python utiliza o hash em suas tabelas internas para encontrar o valor do objeto rapidamente. Como nosso objeto não tem hash, o erro acima é lançado.

Para adicionar um hash a um objeto implementa-se as funções __hash__ e __eq__ simultaneamente (caso esteja utilizando Python 2 a função __ne__ também deve ser adicionada). No exemplo a seguir modificamos nossa classe anterior para adquirir essa propriedade.

@dataclass
class Pessoa:
    nome: str
    cpf: str

    def __hash__(self):
        return hash(self.cpf)

    def __eq__(self, other):
        mesma_classe = self.__class__ == other.__class__
        mesmo_cpf = self.cpf == other.cpf

        return mesma_classe and mesmo_cpf

p = Pessoa(nome="Ítalo Epifânio", cpf="101.010.101-01")
pessoas = set()
pessoas.add(p)
Enter fullscreen mode Exit fullscreen mode

Agora o código consegue ser executado sem o erro anterior. Se executarmos print(pessoas) veremos que o conjunto contém o seguinte valor:

{Pessoa(nome='Ítalo Epifânio', cpf=1010101010)}
Enter fullscreen mode Exit fullscreen mode

Um objeto é dito hashable se o valor de hash nunca é modificado durante sua fase de vida
-- Python Docs

Apenas implementar os dunder métodos acima não garante que o objeto é hashable. É preciso garantir o valor de hash desse objeto jamais sejá alterado pois isso pode levar a comportamentos não esperados, por exemplo:

p = Pessoa(nome="Ítalo Epifânio", cpf="101.010.101-01")
pessoas = set()
pessoas.add(p)
print(p in pessoas) # retorna True
p.cpf = "999.999.999-99"
print(p in pessoas) # retorna False
print(pessoas) # retorna {Pessoa(nome='Ítalo Epifânio', cpf='999.999.999-99')}
Enter fullscreen mode Exit fullscreen mode

Ao alterar o cpf do objeto p note que o objeto não é mais encontrado na estrutura de dados conjunto (a segunda chamada do print(p in pessoas) retorna falso) e quando lista-se os valores do conjunto pessoas nota-se que há ainda um valor lá.

Em resumo: você não pode basear hash de objetos em valores mutáveis. Se o atributo de um objeto pode ser modificado durante seu ciclo de vida comportamentos inesperados podem acontecer.

Top comments (0)

We are hiring! Do you want to be our Senior Platform Engineer? We're hiring for a Senior Platform Engineer and would love for you to apply.

Head here to learn more about who we're looking for.