Tipos que definem `__eq__` são unhashable?
-
05-07-2019 - |
Pergunta
Eu tinha um bug estranho ao portar um recurso para o Python 3.1 fork do meu programa. Eu reduzi-lo para a seguinte hipótese:
Em contraste com Python 2.x, em Python 3.x se um objeto tem um método __eq__
é automaticamente unhashable.
Isso é verdade?
Eis o que acontece no Python 3.1:
>>> class O(object):
... def __eq__(self, other):
... return 'whatever'
...
>>> o = O()
>>> d = {o: 0}
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
d = {o: 0}
TypeError: unhashable type: 'O'
O follow-up questão é: como faço para resolver o meu problema pessoal? Eu tenho um ChangeTracker
objeto que armazena um WeakKeyDictionary
que aponta para diversos objetos, dando para cada o valor de seu despejo picles em um determinado ponto do tempo no passado. Sempre que um objeto existente for verificado, o rastreador mudança diz se seu novo pickle é idêntico ao seu antigo, portanto dizendo se o objeto mudou nesse meio tempo. O problema é que agora não pode até mesmo verificar se o objeto dado está na biblioteca, porque torna gerar uma exceção sobre o objeto sendo unhashable. (Porque ele tem um método __eq__
.) Como posso resolver isso?
Solução
Sim, se você definir __eq__
, o __hash__
padrão (ou seja, hash o endereço do objeto na memória) vai embora. Isto é importante porque as necessidades de hash para ser consistente com a igualdade:. Objetos iguais precisa de hash o mesmo
A solução é simples:. Apenas definir __hash__
juntamente com a definição __eq__
Outras dicas
Este parágrafo de http://docs.python.org/3.1 /reference/datamodel.html#object. de hash
Se uma classe que substitui
__eq__()
precisa manter a implementação de__hash__()
de uma classe pai, o intérprete deve ser dito isso explicitamente, definindo__hash__ = <ParentClass>.__hash__
. Caso contrário, o herança de__hash__()
será bloqueadas, como se tivesse sido__hash__
explicitamente definido como Nenhum.
Verifique o manual Python 3 no object.__hash__
:
Se uma classe não define um método
__eq__()
não deve definir uma operação__hash__()
qualquer um; se define__eq__()
mas não__hash__()
, suas instâncias não será utilizável como itens em coleções Hashable.
A ênfase é minha.
Se você quer ser preguiçoso, parece que você pode apenas definir __hash__(self)
para id(self)
retorno:
classes definidas pelo usuário têm
__eq__()
e__hash__()
métodos por padrão; com eles, todos os objetos comparar desigual (exceto com eles mesmos) ex.__hash__()
retornosid(x)
.
Não sou especialista python, mas não faria sentido que, quando você definir um método-eq, você também tem que definir um método de hash-bem (que calcula o valor de hash para um objeto) Caso contrário, o mecanismo de hashing não saberia se ele atingiu o mesmo objeto, ou um objeto diferente com apenas o mesmo hash de valor. Na verdade, é o contrário, ele provavelmente iria acabar computação diferentes valores de hash para objetos considerados iguais por seu método __eq__
.
Eu não tenho idéia o que isso função hash é chamado, porém, __hash__
talvez? :)