Типы, которые определяют `__eq__`, не поддаются хэшированию?
-
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__
возможно? :) Р>