Типы, которые определяют `__eq__`, не поддаются хэшированию?

StackOverflow https://stackoverflow.com/questions/1608842

  •  05-07-2019
  •  | 
  •  

Вопрос

У меня возникла странная ошибка при переносе функции в форк Python 3.1 моей программы.Я сузил круг поисков до следующей гипотезы:

В отличие от Python 2.x, в Python 3.x, если объект имеет __eq__ метод автоматически становится недоступным для хэширования.

Это правда?

Вот что происходит в 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'

Следующий вопрос заключается в том, как мне решить свою личную проблему?У меня есть объект ChangeTracker который хранит WeakKeyDictionary это указывает на несколько объектов, давая для каждого значение их пикл-дампа в определенный момент времени в прошлом.Всякий раз, когда регистрируется существующий объект, система отслеживания изменений сообщает, идентичен ли его новый пикл-код старому, следовательно, сообщая, изменился ли объект за это время.Проблема в том, что теперь я даже не могу проверить, есть ли данный объект в библиотеке, потому что это заставляет его вызывать исключение о том, что объект не является хэшируемым.(Потому что у него есть __eq__ метод.) Как я могу обойти это?

Это было полезно?

Решение

Да, если вы определите __eq__, значение по умолчанию __hash__ (а именно, хэширование адреса объекта в памяти) исчезнет. Это важно, потому что хэширование должно соответствовать равенству: одинаковые объекты должны хэшировать одно и то же.

Решение простое: просто определите <=> вместе с определением <=>.

Другие советы

Этот абзац из http://docs.python.org/3.1/reference/datamodel.html#object .хэш

Если класс, который переопределяет __eq__() необходимо сохранить реализацию __hash__() из родительского класса интерпретатору должно быть сообщено об этом явно, установив __hash__ = <ParentClass>.__hash__.В противном случае наследование __hash__() будет заблокирован, точно так же, как если бы __hash__ было явно установлено значение None.

Ознакомьтесь с руководством по Python 3 на object.__hash__ :

  

Если класс не определяет метод __eq__(), он также не должен определять операцию __hash__(); если он определяет __hash__(self), но не id(self), его экземпляры не будут использоваться в качестве элементов в хэш-коллекциях.

Акцент мой.

Если вы хотите быть ленивым, похоже, вы можете просто определить x.__hash__(), чтобы вернуть id(x):

  

Пользовательские классы по умолчанию имеют методы <=> и <=>; с ними все объекты сравниваются неравно (кроме самих себя) и <=> возвращает <=>.

Я не эксперт по Python, но не имеет ли смысла, что, когда вы определяете eq-метод, вы также должны определить и хеш-метод (который вычисляет значение хеш-функции для объекта). В противном случае, механизм хеширования не будет знать, попадет ли он в тот же объект или в другой объект с таким же хеш-значением. На самом деле, все наоборот, возможно, в конечном итоге вы вычислите разные значения хеш-функции для объектов, которые равны вашему __eq__ методу.

Я понятия не имею, как называется эта хеш-функция, __hash__ возможно? :)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top