Python 2: различное значение ключевого слова «in» для множеств и списков

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

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, особенно когда у тестируемого типа определены методы равенства.

 Karl Knechtel13 февр. 2012 г., 08:01
Кстати, вы знаете, что вашsomeattribute воткласс атрибут, а неэкземпляр атрибут, верно? Выимеют слышал о__init__, правильно?
 mskel13 февр. 2012 г., 05:18
Спасибо всем, теперь все ясно.
 DSM13 февр. 2012 г., 05:08
Видетьstackoverflow.com/questions/7549709/... .. между прочим, в Python 3 выполнение этого кода намекает на то, что происходит: "Ошибка типа: не подлежащий обработке тип: 'SomeClass'"
 mskel14 февр. 2012 г., 04:30
@Karl: да, отредактировано для реализма

Ответы на вопрос(3)

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

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

Решение Вопроса

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

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

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

def __hash__(self):
    return hash(self.someattribute)
 lvc13 февр. 2012 г., 05:11
Это одна из вещей, которые Python 3 обрабатывает более четко: он откажется делать набор из любого объекта, который не имеет__hash__(), Python 2 имеет значение по умолчанию__hash__() это отражает идентичность объекта, а не равенство.
 Thomas Wouters13 февр. 2012 г., 21:13
На самом деле, классические классы вели себя одинаково (не определяя__hash__ метод даст вам значение по умолчанию, которое вызвало TypeError, если вы определите__cmp__ и / или__eq__,) но затем были введены классы нового стиля (в Python 2.2), и это поведение не было правильно скопировано. Это упущение пропущено достаточно, выпуски, которые могут его изменить, могут испортить слишком много кода, поэтому исправление было отложено до Python 3.

Ваш ответ на вопрос