Oi Lucas, tudo bem?
Quando implementamos o __eq_ é interessante que também implementemos o __ne__. Apesar de que a partir do Python 3, o operador != retorna automaticamente o inverso do retorno do método __eq__(), se o método __ne__() não tiver sido implementado.
Porém, se sua classe herdar de outra em que o método __ne__() é definido (como é nos tipos primitivos), o comportamento do != se baseará no que é especificado nesse método, não no inverso do __eq__()! Por conta disso, é recomendável sempre declarar o método __ne__() quando quisermos implementar o __eq__(), tanto pela compatibilidade com o Python 2, quanto para evitar possíveis problemas em nosso código.
Quanto ao uso do __hash__ , ele retorna o valor do hash de um objeto se ele tiver um. E sua implementação juntamente com o __eq_ é válida quando:
- Formos substituir o
__eq__ e o objeto for imutável.
Porém, a partir do Python 3, ele automaticamente torna as classes laváveis(um objeto é lavável se tiver um valor de hash que nunca muda durante sua vida útil) se você implementar o __eq__ mas não __hash__, então não há muitas preocupações quanto a isso.
Deixo abaixo alguns artigos sobre o assunto:
Qualquer dúvida estou a disposição. Espero ter ajudado. Abraços e bons estudos!