Неожиданное поведение хэш-функции при использовании set в классе

#python-3.x #hash #set

#python-3.x #хэш #set

Вопрос:

У меня есть 2 класса ниже. Поскольку в наборах могут храниться только хешируемые объекты, я определяю хеш-функцию для класса Person. Каждый раз, когда я добавляю Person в объект group, вызывается хэш-функция, которая генерирует хэш, используя только имя. Может ли кто-нибудь объяснить мне, почему, когда я добавляю объект p2 с тем же именем, что и p1, но разного возраста для группы, python все равно добавляет его к групповому объекту, даже если 2 объекта имеют одинаковый хэш. Разве python не должен при добавлении объекта в set проверять хэш и либо добавлять, либо заменять объект? Если я попытаюсь добавить объект p3, точно такой же, как p1 или p2, он не будет добавлен.

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

    def __hash__(self):
        print(f'inhash: {self.name}, {self.age}, {hash(self.name)}')
        return hash(self.name)

    def __repr__(self):
        return f'<Person({self.name}, {self.age})>'


class Group:
    def __init__(self, group_name):
        self.group_name = group_name
        self.persons = set()

    def add_person(self, person):
        self.persons.add(person)

    def __repr__(self):
        return f'<Group({self.group_name}, {self.persons})>'


p1 = Person('p1', 10)
p2 = Person('p1', 20)

print("persons p1, p2, p3:", (p1, p2))
group = Group('pp')
group.add_person(p1)
print(group)
group.add_person(p2)
print(group)
  

Комментарии:

1. Вам необходимо реализовать __eq__

Ответ №1:

Из документов __hash__ :

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

Акцент мой.

Вам также необходимо предоставить __eq__ реализацию, чтобы она могла правильно различать объекты.