Trilha de aprendizado · Nível 11 · Tutorial 3

Manter igualdade e hash coerentes

Ao concluir, você poderá decidir se uma classe deve aceitar hash e implementar esse comportamento sem comprometer consultas em dicionários e conjuntos.

  • Nível: Avançado
  • Duração: 20 min
  • 7 passos
Manter igualdade e hash coerentes

O que você vai percorrer

  1. O contrato que dicionários e conjuntos exigem Entenda as garantias que permitem usar objetos com segurança em dicionários e conjuntos. 3 min
  2. Quando uma classe aceita hash Preveja se instâncias de uma classe podem ser usadas em operações que exigem hash a partir de suas definições de igualdade e hash. 3 min
  3. Separar estado mutável de identidade de valor Decida quando uma alteração de estado é compatível com o uso de uma instância como chave de dicionário ou elemento de conjunto. 3 min
  4. Compor o hash com os campos certos Implemente um hash coerente com a igualdade de valor e mantenha esse contrato estável. 4 min
  5. Respeitar os limites de uso do hash Use hash para buscas em memória, não como identificador salvo ou número permanente. 2 min
  6. Testar o contrato pelo uso real Execute um script autocontido para verificar o comportamento de objetos equivalentes em dicionários e conjuntos. 4 min
  7. Aplicação final: escolher, corrigir e validar Aplique o contrato de igualdade e hash em dois modelos e valide o comportamento em dicionários e conjuntos. 3 min

O que você vai aprender

  • Explicar por que objetos iguais precisam produzir o mesmo hash, mas hashes iguais não garantem igualdade.
  • Implementar __hash__ com base em campos estáveis que participam da igualdade.
  • Impedir o uso como chave quando mudanças no estado puderem invalidar o contrato de igualdade e hash.
  • Testar consultas e eliminação de duplicatas usando instâncias equivalentes.

Antes de começar

  • Definir igualdade e ordenação entre objetos
  • Consultar e atualizar dados com dicionários
  • Eliminar duplicatas e comparar grupos com conjuntos
  • Controlar referências e cópias de coleções
  • Preservar regras de validade em objetos

Passo 1 de 7

O contrato que dicionários e conjuntos exigem

Entenda as garantias que permitem usar objetos com segurança em dicionários e conjuntos.

Hash: uma pista inteira para localizar objetos

O que `hash()` pede ao objeto

A chamada hash(objeto) usa o comportamento __hash__ do objeto e produz um inteiro. Esse inteiro funciona como uma pista para dicionários e conjuntos encontrarem rapidamente uma região provável onde procurar.

Para um objeto ser hashável, seu hash deve permanecer estável durante toda a vida em que ele puder ser usado como chave de dicionário ou elemento de conjunto.

Hash orienta; igualdade confirma

A busca usa duas etapas conceituais: primeiro o hash reduz a área de procura; depois a igualdade decide se o objeto encontrado é realmente o procurado.

Diagrama com um objeto de consulta passando por uma etapa de hash que o direciona a uma pequena área de candidatos e, em seguida, por uma comparação de igualdade que identifica o objeto equivalente.

O hash direciona a busca; == confirma a correspondência.

A regra indispensável

Exemplo

Igualdade impõe o mesmo hash

Se a == b é verdadeiro para dois objetos hasháveis, então hash(a) == hash(b) também precisa ser verdadeiro.

Em forma curta:

a == b → hash(a) == hash(b)

O sentido inverso não é exigido. Dois objetos podem ter o mesmo hash e ainda assim serem diferentes.

Dica

Não inverta a implicação

Use o hash para orientar uma consulta, não para concluir igualdade. Um hash igual é apenas uma possibilidade de correspondência; a comparação de igualdade ainda é necessária.

Colisões são permitidas

Mesmo hash, objetos diferentes

Uma colisão acontece quando objetos diferentes produzem o mesmo hash. Isso não viola o contrato: o dicionário ou conjunto compara os candidatos com igualdade para distingui-los.

Portanto, hashes não precisam ser únicos. O requisito é que objetos iguais nunca recebam hashes diferentes.

Uma colisão não confunde a igualdade

Mesmo recebendo a mesma pista de hash, dois objetos distintos continuam separados porque a etapa seguinte verifica igualdade.

Comparação lado a lado de dois cartões de objetos visualmente diferentes chegando ao mesmo token numérico de hash e depois sendo separados por um teste de igualdade falso.

Colisão: mesmo hash; igualdade falsa; objetos distintos.

Verifique o contrato

Relação obrigatória

Se dois objetos hasháveis são iguais por ==, eles devem produzir o mesmo valor em hash().

Relação que não é garantida

Se hash(a) == hash(b), então a == b necessariamente é verdadeiro.

Passo 2 de 7

Quando uma classe aceita hash

Preveja se instâncias de uma classe podem ser usadas em operações que exigem hash a partir de suas definições de igualdade e hash.

O comportamento padrão vem de object

Sem redefinir métodos especiais

Uma classe que mantém os comportamentos padrão de object compara instâncias pela identidade. Nesse caso, ela também é hashável: hash(objeto) devolve um inteiro apropriado para consultas em dicionários e conjuntos.

Não dependa, porém, de hash(objeto) == id(objeto) nem suponha que cada instância tenha um hash exclusivo. O contrato só exige coerência com a igualdade.

Classe com comportamento padrão

python
class Sessao:
    pass

primeira = Sessao()
segunda = Sessao()

print(primeira == segunda)  # False: são instâncias distintas
print(hash(primeira))       # inteiro
print({primeira, segunda})  # as duas instâncias podem coexistir

Identidade: instâncias separadas

Diagrama com duas instâncias distintas da mesma classe, cada uma apontando para uma posição diferente, ambas aceitas como elementos de um conjunto.

Com igualdade padrão, cada instância é comparada com base em sua própria identidade.

Definir __eq__ muda a disponibilidade de hash

Proteção automática do Python

Ao definir __eq__ para criar igualdade de valor e não fornecer __hash__, o Python torna a classe não hashável automaticamente. Isso evita que uma igualdade personalizada seja combinada por acidente com um hash herdado incompatível.

Assim, hash(instancia), usar a instância como chave ou adicioná-la a um conjunto causam TypeError.

__eq__ sem __hash__

python
class Codigo:
    def __init__(self, valor):
        self.valor = valor

    def __eq__(self, outro):
        if not isinstance(outro, Codigo):
            return NotImplemented
        return self.valor == outro.valor

item = Codigo("A-17")
print(hash(item))  # TypeError: unhashable type: 'Codigo'

A decisão automática

Fluxo visual mostrando uma classe com igualdade padrão seguindo para hash disponível e uma classe com igualdade personalizada sem hash seguindo para hash bloqueado.

Personalizar __eq__ sem definir __hash__ bloqueia o uso em estruturas baseadas em hash.

Declaração explícita e armadilha comum

Quando a classe não deve ser hashável

Escreva __hash__ = None para declarar explicitamente que as instâncias não são hasháveis. Essa escolha também produz TypeError nas operações que exigem hash.

Evite a aparente correção __hash__ = object.__hash__ em uma classe com igualdade de valor. Ela recupera um hash ligado à identidade; duas instâncias diferentes podem ser iguais e ainda ter hashes diferentes, quebrando o contrato.

Atenção

Não restaure o hash de identidade por reflexo

object.__hash__ só é compatível quando a igualdade também segue a identidade. Se __eq__ diz que objetos distintos podem ser iguais, atribuir object.__hash__ não é uma solução geral.

Não hashável por decisão explícita

python
class Rascunho:
    __hash__ = None

rascunho = Rascunho()

# hash(rascunho)          # TypeError
# {rascunho}              # TypeError
# {rascunho: "aberto"}   # TypeError

Associe cada definição ao comportamento

Previsão de hash

Relacione cada forma de classe ao resultado esperado para hash(instancia).

Toque em um item e depois no par correspondente.

Passo 3 de 7

Separar estado mutável de identidade de valor

Decida quando uma alteração de estado é compatível com o uso de uma instância como chave de dicionário ou elemento de conjunto.

O que pode mudar?

Estado não é tudo igual

Uma instância pode ter atributos mutáveis e ainda ser hashável — desde que esses atributos não definam sua igualdade nem influenciem seu hash.

Separe mentalmente dois grupos:

  • Campos de identidade de valor: determinam quando duas instâncias representam o mesmo valor. Se participam de __eq__, precisam permanecer estáveis enquanto o objeto estiver em um dicionário ou conjunto.
  • Metadados: informações editáveis que não participam da igualdade nem do hash, como uma observação de exibição ou a data da última consulta.

A pergunta decisiva não é “o objeto muda?”, mas sim: “os campos que definem seu valor podem mudar?”

Identidade versus metadados

Observe quais alterações afetam a segurança da chave.

Diagrama de um item de catálogo com código e edição como campos de identidade destacados, e uma observação como metadado separado. Uma seta mostra que editar apenas a observação mantém a identidade; alterar o código muda a identidade.

Alterar metadados pode ser seguro; alterar campos usados na igualdade ou no hash compromete o contrato.

A alteração que quebra a busca

Depois da inserção, não mude a identidade

Dicionários e conjuntos usam o hash para orientar onde procurar e depois verificam a igualdade. Se um campo de identidade muda após a inserção, o objeto pode passar a produzir outro hash ou deixar de ser igual à instância equivalente usada na consulta.

O resultado não deve ser tratado como uma falha previsível única: uma consulta pode deixar de localizar a chave, uma remoção pode falhar, ou a coleção pode aparentar funcionar em algum caso. Em todos eles, o contrato já foi violado.

Exemplo de estado incoerente

Este exemplo pressupõe uma classe já hashável cujo código participa da igualdade e do hash.

python
# Suponha que codigo participe de __eq__ e de __hash__.
referencia = Referencia("A-10")
estoque = {referencia: 5}

referencia.codigo = "B-20"  # altera a identidade após a inserção

# Não se deve confiar no comportamento destas operações:
print(estoque.get(referencia))
print(Referencia("A-10") in estoque)
print(Referencia("B-20") in estoque)

Atenção

Não tente compensar

Guardar o hash calculado antes da mudança não resolve o problema. A igualdade passaria a refletir a nova identidade, enquanto o hash guardado refletiria a antiga. Objetos iguais precisam produzir hashes iguais; preservar um hash antigo quebra essa relação.

Escolha a política de modelagem

Três decisões coerentes

Escolha conforme o contrato público da classe:

  1. Campos de valor estáveis: a classe pode ser hashável, se igualdade e hash dependerem somente desses campos.
  2. Campos de valor editáveis: declare __hash__ = None. Assim, a classe não pode ser inserida em conjuntos nem usada como chave, evitando um uso inseguro.
  3. Identidade externa imutável: use uma chave separada, como uma tupla com os valores relevantes no instante da criação, e mantenha o objeto mutável fora do papel de chave.

A terceira opção é útil quando o cadastro precisa mudar, mas uma consulta precisa de uma referência estável.

Decida pelo campo relevante

Uma classe Livro compara apenas isbn e calcula seu hash apenas a partir de isbn. O atributo nota_interna pode ser alterado livremente e não participa dessas operações. Qual política é adequada?

Decisão antes da implementação

Justifique a política

Um CadastroCliente considera dois clientes iguais quando possuem o mesmo email, mas o e-mail pode ser corrigido depois. Qual política de hash você escolheria e por quê?

Escreva pelo menos 40 caracteres (0/40).

Passo 4 de 7

Compor o hash com os campos certos

Implemente um hash coerente com a igualdade de valor e mantenha esse contrato estável.

Campos que definem o valor

A mesma seleção usada pela igualdade

Quando a igualdade já considera dois campos de valor, componha o hash com esses mesmos campos. A forma usual é aplicar hash() a uma tupla: hash((campo1, campo2)).

Na referência de catálogo, codigo e edicao definem o valor. Já observacao é um metadado editável: duas referências podem ser iguais mesmo com observações diferentes. Portanto, ela fica fora de __eq__ e de __hash__.

Fluxo da identidade de valor

Os dois campos estáveis seguem juntos para igualdade e hash; o metadado editável permanece fora desse contrato.

Diagrama mostrando dois campos estruturais conectados a uma tupla e a um símbolo de hash, enquanto um terceiro campo de metadado aparece separado, sem conexão.

Use no hash exatamente os campos que participam da identidade de valor.

Implementação alinhada

Uma classe completa

O __eq__ abaixo já está alinhado ao domínio. O __hash__ apenas reutiliza a mesma dupla de atributos, sem combinar números de hash manualmente.

referencia_catalogo.py

python
class ReferenciaCatalogo:
    def __init__(self, codigo, edicao, observacao=""):
        self._codigo = codigo
        self._edicao = edicao
        self.observacao = observacao

    def __eq__(self, other):
        if not isinstance(other, ReferenciaCatalogo):
            return NotImplemented
        return (
            self._codigo,
            self._edicao,
        ) == (
            other._codigo,
            other._edicao,
        )

    def __hash__(self):
        return hash((self._codigo, self._edicao))

    def editar_observacao(self, nova_observacao):
        self.observacao = nova_observacao


primeira = ReferenciaCatalogo("BK-204", 3, "Prateleira A")
equivalente = ReferenciaCatalogo("BK-204", 3, "Em revisão")

print(primeira == equivalente)          # True
print(hash(primeira) == hash(equivalente))  # True

indice = {primeira: "disponível"}
print(indice[equivalente])               # disponível

primeira.editar_observacao("Reservado")
print(indice[equivalente])               # disponível

Hashável não basta: precisa permanecer estável

Dica

Contrato da interface

Nesta classe, a operação pública de alteração muda somente observacao. Assim, ela não muda igualdade nem hash. Os campos _codigo e _edicao devem ser tratados como estáveis durante a vida da instância usada como chave ou elemento de conjunto.

Atenção

Subscritos não congelam atributos

Os nomes _codigo e _edicao são uma convenção, não uma barreira: ainda seria possível reatribuí-los diretamente. Do mesmo modo, guardar valores em uma tupla não impede reatribuir o atributo que aponta para ela. A segurança vem do contrato de uso da classe: não ofereça operações que alterem seus campos de valor e não os modifique após inserir o objeto em uma coleção hashada.

Cada componente também deve ser hashável

A tupla externa não resolve componentes não hasháveis. Por exemplo, hash((self._codigo, self.etiquetas)) falha se etiquetas for uma lista, pois listas não são hasháveis. Inclua apenas valores hasháveis que realmente participem da igualdade; não inclua um campo só porque ele existe na instância.

Complete a composição

Método __hash__

Complete a expressão de retorno:

def __hash__(self):
    return ____

A igualdade da classe usa self._codigo e self._edicao.

Passo 5 de 7

Respeitar os limites de uso do hash

Use hash para buscas em memória, não como identificador salvo ou número permanente.

Hash serve à execução atual

Estável não significa permanente

O contrato exige que o hash de um objeto hashável permaneça estável durante a vida daquele objeto. Isso permite que dicionários e conjuntos o localizem na execução atual.

Esse contrato não promete que o número retornado por hash() será o mesmo quando você executar o programa novamente. Em especial, hashes de str — e de tuplas que incluem strings — podem mudar entre processos do Python por causa da aleatorização interna.

Mesma referência, processos diferentes

A relação importante é dentro de uma execução: objetos equivalentes devem ter hashes iguais.

Diagrama comparando dois processos Python: em cada um há uma referência de catálogo com os mesmos campos e um valor de hash visualmente diferente; dentro de cada processo, duas instâncias equivalentes apontam para o mesmo compartimento de busca.

O mesmo valor de domínio pode receber números de hash diferentes em execuções distintas sem quebrar o contrato.

Exemplo

Não fixe um número esperado

Evite um teste como este:

assert hash(referencia) == 123456

Esse teste pode falhar em outra execução mesmo que __hash__ esteja correto. Prefira verificar relações do contrato, por exemplo:

assert primeira == segunda
assert hash(primeira) == hash(segunda)

Também não exija hashes diferentes para objetos diferentes: colisões são permitidas.

Salvar campos, não o hash

Referência durável

Se você precisa gravar uma referência em arquivo para encontrá-la depois, salve os próprios campos identificadores do domínio — por exemplo, codigo e edicao —, não o resultado de hash().

Ao carregar os dados em outra execução, reconstrua ou consulte a referência pelos campos. O hash continua sendo um mecanismo interno de consulta em memória.

Exemplo

Registro persistente adequado

Para uma referência cuja igualdade usa codigo e edicao, um JSON pode registrar:

{"codigo": "PY-204", "edicao": 3}

Não use algo como:

{"hash": 481516}

O primeiro formato conserva o significado da referência. O segundo conserva apenas um número sem garantia de repetição nem de unicidade.

Dica

Regra prática

Use hash(objeto) para apoiar dicionários e conjuntos enquanto o programa está em execução. Para salvar, transmitir ou comparar uma referência ao longo do tempo, use os campos que definem essa referência.

Verifique a afirmação

Contrato entre execuções

Se hash(referencia) produz valores diferentes em duas execuções do programa, então __hash__ está necessariamente incorreto.

Passo 6 de 7

Testar o contrato pelo uso real

Execute um script autocontido para verificar o comportamento de objetos equivalentes em dicionários e conjuntos.

O que o teste deve comprovar

Evidências observáveis

Não teste hashes por um número específico: ele pode variar entre execuções. Teste as relações que o contrato exige.

Para duas referências de catálogo equivalentes, verifique:

  • a == b é verdadeiro e hash(a) == hash(b) também;
  • uma chave inserida com a é encontrada e atualizada com b, sem criar outra entrada;
  • um conjunto conserva apenas uma ocorrência;
  • mudar um metadado que não participa da igualdade preserva essas consultas.

Em contraste, uma classe de valor mutável declarada com __hash__ = None deve gerar TypeError ao ser usada como chave ou elemento de conjunto.

Consulta por instância equivalente

Diagrama mostrando uma instância de ReferenciaCatalogo inserida como chave de dicionário e uma segunda instância equivalente encontrando e atualizando a mesma entrada; o metadado editável aparece separado dos campos de valor.

O hash direciona a busca; a igualdade confirma que a instância equivalente representa a mesma chave.

Dica

Não inverta a regra

O teste hash(a) == hash(b) não prova que a == b: colisões são permitidas. Por isso, o script também testa uma colisão controlada com objetos diferentes.

Script completo de verificação

Execute localmente

Crie um arquivo chamado verificar_hash.py, copie o código abaixo e rode no seu computador com python verificar_hash.py. Se tudo estiver coerente, a única saída será a mensagem final.

verificar_hash.py

python
class ReferenciaCatalogo:
    def __init__(self, codigo, edicao, observacao):
        self.codigo = codigo
        self.edicao = edicao
        self.observacao = observacao  # metadado editável

    def __eq__(self, other):
        if not isinstance(other, ReferenciaCatalogo):
            return NotImplemented
        return (self.codigo, self.edicao) == (other.codigo, other.edicao)

    def __hash__(self):
        return hash((self.codigo, self.edicao))


class CadastroMutavel:
    def __init__(self, email):
        self.email = email

    def __eq__(self, other):
        if not isinstance(other, CadastroMutavel):
            return NotImplemented
        return self.email == other.email

    __hash__ = None


class ColisaoDeTeste:
    def __init__(self, valor):
        self.valor = valor

    def __eq__(self, other):
        if not isinstance(other, ColisaoDeTeste):
            return NotImplemented
        return self.valor == other.valor

    def __hash__(self):
        return 0  # somente para testar uma colisão controlada


primeira = ReferenciaCatalogo("PY-101", 3, "prateleira A")
equivalente = ReferenciaCatalogo("PY-101", 3, "revisada")

# Objetos distintos, mas mesmo valor para igualdade e hash.
assert primeira is not equivalente
assert primeira == equivalente
assert hash(primeira) == hash(equivalente)

# Uma chave equivalente localiza e atualiza a entrada existente.
estoque = {primeira: 4}
assert estoque[equivalente] == 4
estoque[equivalente] = 7
assert len(estoque) == 1
assert estoque[primeira] == 7

# O conjunto elimina a duplicata equivalente.
referencias = {primeira, equivalente}
assert len(referencias) == 1

# Alterar metadado fora da identidade de valor não altera o contrato.
hash_antes = hash(primeira)
primeira.observacao = "emprestada até sexta"
assert primeira == equivalente
assert hash(primeira) == hash_antes
assert estoque[equivalente] == 7
assert len({primeira, equivalente}) == 1

# Uma classe não hashável é rejeitada em dicionários e conjuntos.
cadastro = CadastroMutavel("ana@exemplo.com")
for operacao in (
    lambda: hash(cadastro),
    lambda: {cadastro: "ativo"},
    lambda: {cadastro},
):
    try:
        operacao()
    except TypeError:
        pass
    else:
        raise AssertionError("CadastroMutavel deveria ser não hashável")

# Colisão não transforma valores diferentes em objetos iguais.
x = ColisaoDeTeste("A")
y = ColisaoDeTeste("B")
assert hash(x) == hash(y)
assert x != y
assert len({x, y}) == 2
assert {x: "primeiro", y: "segundo"}[y] == "segundo"

print("Todas as verificações passaram.")

Atenção

O hash constante é só uma prova

ColisaoDeTeste.__hash__ retorna 0 apenas para demonstrar que o dicionário e o conjunto ainda distinguem valores pela igualdade. Não use hash constante como implementação normal: ele não representa os campos de valor e prejudica as consultas.

Relate as evidências

Após executar

Execute o script no seu computador. O que a saída e as verificações mostraram sobre: consulta por chave equivalente, remoção de duplicata, mudança de metadado e rejeição de CadastroMutavel?

Escreva pelo menos 80 caracteres (0/80).

Passo 7 de 7

Aplicação final: escolher, corrigir e validar

Aplique o contrato de igualdade e hash em dois modelos e valide o comportamento em dicionários e conjuntos.

Escolha a política pelo estado que define o valor

Dois modelos, duas decisões

Uma ReferênciaCatalogo é identificada por codigo e versao, campos que a interface mantém estáveis. Ela pode ser hashável: igualdade e hash usam exatamente esses campos.

Um CadastroContato é identificado por email, mas esse dado pode ser alterado. Como a identidade de valor pode mudar, a decisão segura é declarar __hash__ = None. Um atributo mutável, como uma observação, não impediria o hash por si só; o problema é a mutação de um campo que participa da igualdade.

Comparação de modelagens

Diagrama lado a lado: uma referência de catálogo com dois campos estáveis ligados a um dicionário e a um conjunto; ao lado, um cadastro com um campo identificador editável e um símbolo indicando que ele não deve ser usado como chave.

Campos estáveis de valor podem compor o hash; campos de igualdade que podem mudar exigem uma política não hashável.

Código completo corrigido

Aplique cada decisão no ponto certo

O primeiro modelo compõe o hash com uma tupla dos campos relevantes e hasháveis. O segundo torna a recusa explícita. Copie o script para um arquivo, execute-o no seu computador e confira as mensagens exibidas.

catalogo_e_cadastro.py

python
class ReferenciaCatalogo:
    def __init__(self, codigo, versao, observacao):
        self.codigo = codigo
        self.versao = versao
        self.observacao = observacao

    def __eq__(self, outro):
        if not isinstance(outro, ReferenciaCatalogo):
            return NotImplemented
        return (self.codigo, self.versao) == (outro.codigo, outro.versao)

    def __hash__(self):
        return hash((self.codigo, self.versao))


class CadastroContato:
    def __init__(self, email, nome):
        self.email = email
        self.nome = nome

    def __eq__(self, outro):
        if not isinstance(outro, CadastroContato):
            return NotImplemented
        return self.email == outro.email

    __hash__ = None


original = ReferenciaCatalogo("LIV-42", 3, "primeira revisão")
equivalente = ReferenciaCatalogo("LIV-42", 3, "texto atualizado")

indice = {original: "disponível"}
print(original == equivalente)                 # True
print(hash(original) == hash(equivalente))     # True
print(indice[equivalente])                     # disponível
print(len({original, equivalente}))            # 1

original.observacao = "revisão editorial"
print(indice[equivalente])                     # disponível

contato = CadastroContato("ana@exemplo.com", "Ana")
try:
    {contato}
except TypeError as erro:
    print(type(erro).__name__)                 # TypeError

Correção rápida

Complete a política do cadastro

Para impedir o uso de CadastroContato como chave enquanto email pode mudar, complete a linha:

_____ = None

Revisão e validação final

Justifique a decisão

Para cada classe do script, explique a política de hash escolhida e cite duas verificações que demonstram que a decisão está correta.

Escreva pelo menos 80 caracteres (0/80).

Resumo

Checklist antes de tornar uma classe hashável

  • Liste os campos que determinam a igualdade de valor.
  • Só implemente __hash__ se esses campos permanecerem estáveis enquanto a instância puder estar em dicionários ou conjuntos.
  • Monte o hash com hash((campo1, campo2, ...)); cada componente precisa ser hashável.
  • Objetos iguais devem gerar hashes iguais; hashes iguais não provam igualdade.
  • Teste consulta por uma instância equivalente e eliminação de duplicatas em um conjunto.
  • Não use o resultado de hash() como identificador persistente entre execuções.
  • Se a identidade de valor puder mudar, declare __hash__ = None.

Tutorial concluído

Parabéns! Você concluiu: Manter igualdade e hash coerentes

Você concluiu o tutorial. Agora consegue decidir quando uma classe pode ser hashável, implementar um hash coerente e validar o comportamento real em dicionários e conjuntos.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso