Python 2: различное значение ключевого слова «in» для множеств и списков - PullRequest
8 голосов
/ 13 февраля 2012

Рассмотрим этот фрагмент:

class SomeClass(object):

    def __init__(self, someattribute="somevalue"):
        self.someattribute = someattribute

    def __eq__(self, other):
        return self.someattribute == other.someattribute

    def __ne__(self, other):
        return not self.__eq__(other)

list_of_objects = [SomeClass()]
print(SomeClass() in list_of_objects)

set_of_objects = set([SomeClass()])
print(SomeClass() in set_of_objects)

, который оценивается как:

True
False

Может кто-нибудь объяснить, почему ключевое слово «in» имеет другое значение для наборов и списков?Я ожидал бы, что оба вернут True, особенно если для тестируемого типа определены методы равенства.

Ответы [ 3 ]

16 голосов
/ 13 февраля 2012

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

Ваш класс определяет __eq__, но не определяет __hash__, и поэтому не будет работать должным образом для наборов или в качестве ключей словарей. Правило для __eq__ и __hash__ состоит в том, что два объекта, которые __eq__ как Истина, также должны иметь равные хэши. По умолчанию объекты хэшируются в зависимости от их адреса памяти. Таким образом, ваши два объекта, которые равны по вашему определению, не предоставляют одинаковый хэш, поэтому они нарушают правило о __eq__ и __hash__.

Если вы предоставите реализацию __hash__, она будет работать нормально. Для вашего примера кода это может быть:

def __hash__(self):
    return hash(self.someattribute)
3 голосов
/ 13 февраля 2012

В большинстве хеш-таблиц, в том числе в Python, если вы переопределяете метод равенства, вы должны переопределить метод хеширования (в Python это __hash__). Оператор in для списков просто проверяет равенство с каждым элементом списка, причем оператор in для наборов сначала хэширует искомый объект, проверяет объект в этом слоте хеш-таблицы, а затем проверяет равенство если есть что-нибудь в слоте. Таким образом, если вы переопределяете __eq__ без переопределения __hash__, вы не можете быть уверены, что оператор in для наборов проверит в правильном слоте.

1 голос
/ 13 февраля 2012

Определите __hash__() метод, который соответствует вашему __eq__() методу. Пример .

...