То же хеш-значение, но не тот же объект после переопределения __hash__ - PullRequest
2 голосов
/ 14 февраля 2011

У меня проблема с правильным хэшированием моих объектов. Рассмотрим следующий код:

class Foo:
    def __init__(self, bar):
        self.keys = list(bar.keys())
        self.values = list(bar.values())    
    def __str__(self):
        return ', '.join( '%s: %s' % z for z in zip(self.keys, self.values))    
    def __hash__(self):
        return hash(str(self))

if __name__ == '__main__':
    result = set()
    d = { 1: 2, 3: 4, 5: 6, 7: 8 }
    for i in range(10):
        result.add(Foo(d))
    for r in result:
        print r, hash(r)

Я ожидаю, что результирующий набор будет содержать один элемент, поскольку все добавленные объекты Foo имеют одинаковое содержимое и, следовательно, одинаковый хэш.

Однако, это результат:

misha@misha-K42Jr:~/Desktop/stackoverflow$ python hashproblem.py 
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338
1: 2, 3: 4, 5: 6, 7: 8 2131119371379196338

В чем здесь проблема? Хеши do выглядят одинаково, поэтому разве они не должны рассматриваться как дубликаты встроенным объектом set? Почему набор содержит дубликаты?

Я заметил, что если при добавлении элементов в набор я использую str(Foo(d)) вместо Foo(d), все будет работать как положено. Почему это важно?

Версия Python:

misha@misha-K42Jr:~/Desktop/stackoverflow$ python --version
Python 2.6.6

Ответы [ 2 ]

4 голосов
/ 14 февраля 2011

См .: http://docs.python.org/glossary.html#term-hashable - вы также захотите внедрить __eq__.

4 голосов
/ 14 февраля 2011

Поскольку метод __hash__ используется только для внутренней хеш-таблицы, вам также необходимо переопределить __eq__.

Только переопределение __eq__ также неверно. Если два объекта равны, т. Е. a.__eq__(b) == True, то оба hash(a) и hash(b) также должны быть равны.

Метод __hash__ по умолчанию:

def __hash__(self):
    return id(self)
...