Метод хэша класса Python и set [дубликат]

этот вопрос уже есть ответ здесь:

Я использую set() и __hash__ метод python класс для предотвращения добавления одного и того же хэш-объекта в set. Согласно документ модели данных python, set() рассмотрите тот же хэш-объект как тот же объект и просто добавьте их один раз.

но он ведет себя по-другому, как показано ниже:

class MyClass(object):

    def __hash__(self):
        return 0

result = set()
result.add(MyClass())
result.add(MyClass())

print(len(result)) # len = 2

в то время как в случае строкового значения он работает правильно.

result.add('aida')
result.add('aida')

print(len(result)) # len = 1

мой вопрос: почему одни и те же объекты хэша не совпадают в наборе?

2 ответов


ваше чтение неверно. The __eq__ метод используется для проверки равенства. В документах просто говорится, что __hash__ значение также должно быть одинаковым для 2 объектов a и b для чего a == b (т. е. a.__eq__(b)) это правда.

это распространенная логическая ошибка:a == b истинно подразумевает это hash(a) == hash(b) тоже верно. Однако импликация не обязательно означает эквивалентности, что в дополнение ранее, hash(a) == hash(b) означает, что a == b.

чтобы сделать все экземпляры MyClass сравнить равными друг другу, вам нужно предоставить __eq__ метод для них; в противном случае Python будет сравнивать их личности. Это может быть:

class MyClass(object):
    def __hash__(self):
        return 0
    def __eq__(self, other):
        # another object is equal to self, iff 
        # it is an instance of MyClass
        return isinstance(other, MyClass)

теперь:

>>> result = set()
>>> result.add(MyClass())
>>> result.add(MyClass())
1

в реальности вы бы базы __hash__ на те свойства объекта, которые используются для __eq__ сравнения, например:

class Person
    def __init__(self, name, ssn):
        self.name = name
        self.ssn = ssn

    def __eq__(self, other):
        return isinstance(other, Person) and self.ssn == other.ssn

    def __hash__(self):
        # use the hashcode of self.ssn since that is used
        # for equality checks as well
        return hash(self.ssn)

p = Person('Foo Bar', 123456789)
q = Person('Fake Name', 123456789)
print(len({p, q})  # 1

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

ваш класс не реализует __eq__, поэтому по умолчанию object.__eq__ используется вместо этого , который возвращает true только если obj1 is obj2 тоже верно. Другими словами, два экземпляра считаются равными, только если они тот же самый экземпляр.

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

Добавить свой пользовательский __eq__ метод, который возвращает True когда предполагается, что два экземпляра равны:

def __eq__(self, other):
    if not isinstance(other, type(self)):
        return False
    # all instances of this class are considered equal to one another
    return True