2009-10-22 2 views
49

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

В отличие от 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__.) Как я могу обойти это?

+3

Что произойдет, если вы предоставите метод '__hash__'? – ndim

ответ

50

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

Решение простое: просто определите __hash__ вместе с определением __eq__.

+0

Позвольте мне добавить для потомков: Я определил '__hash__' как' return id (self) '. –

+25

@ cool-RR: Использование 'id (self)' в '__hash __()' безусловно ошибочно, если вы не определили '__eq __()' для сравнения по идентичности объекта (что кажется бессмысленным). Как насчет обновления вашего вопроса с помощью реальной реализации '__eq __()', чтобы мы могли предложить '__hash __()' дополнять его? –

+0

Я тоже пытался решить эту проблему; Я придумал это: 'def __eq __(): return self .__ dict__ == other .__ dict__' и 'def __hash __(): return hash (self .__ dict __. Values ​​())' –

1

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

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

+6

FYI: Я рисовал это когда-то (http://www.mindmeister.com/10510492/python-underscore): образ всех подчёркнутых методов в python. – jldupont

+2

Это наоборот. Если два объекта имеют одно и то же значение хэша, но не равны - это нормально. «Хеширующий механизм» (т. Е. Словарь) сначала проверяет хэш, а на одном и том же хэше также сравнивается для равенства. Реальная проблема встречается иначе: round: object hash по-разному, но все равно сравнивает то же самое. Словарь должен найти их, но не (или вы можете получить дубликаты ключей в словаре). –

+0

@Martin v. Löwis: Я понял это и добавил, что в конце, или есть тонкая разница в том, что я сказал? – falstro

3

Проверьте руководство Python 3 на object.__hash__:

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

Акцент делается на моем.

Если вы хотите быть ленивыми, это звучит, как вы можете просто определить __hash__(self) вернуться id(self):

определяемого пользователем классы __eq__() и __hash__() методы по умолчанию; с ними все объекты сравниваются неравномерно (кроме самих себя) и x.__hash__() возвращает id(x).

+3

Несомненно, единственной причиной перегрузки оператора equals является то, что два разных объекта иногда сравниваются с равными (если они не перестают перегружать его). В этом случае нарушается хеш-функция 'return id (self)' (равные объекты должны иметь то же самое, см. Ответ Мартина). Хеш-функция «Я не забочусь» будет «возвращать 1». Это просто и удовлетворяет всем условиям (это просто очень неэффективно!) –

17

Этот пункт из http://docs.python.org/3.1/reference/datamodel.html#object.hash

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

Смежные вопросы