Як набір Python ([]) перевіряє, чи рівні два об’єкти? Які методи потрібно визначити об’єкту, щоб це налаштувати?


83

Мені потрібно створити об'єкт або клас "контейнер" у Python, який зберігає записи інших об'єктів, які я також визначаю. Однією з вимог цього контейнера є те, що якщо два об’єкти вважаються ідентичними, один (або один) видаляється. Моя перша думка полягала в тому, щоб використовувати a set([])як об’єкт, що містить, щоб виконати цю вимогу.

Однак набір не видаляє один з двох однакових екземплярів об'єкта. Що я повинен визначити для його створення?

Ось код Python.

class Item(object):
  def __init__(self, foo, bar):
    self.foo = foo
    self.bar = bar
  def __repr__(self):
    return "Item(%s, %s)" % (self.foo, self.bar)
  def __eq__(self, other):
    if isinstance(other, Item):
      return ((self.foo == other.foo) and (self.bar == other.bar))
    else:
      return False
  def __ne__(self, other):
    return (not self.__eq__(other))

Перекладач

>>> set([Item(1,2), Item(1,2)])
set([Item(1, 2), Item(1, 2)])

Зрозуміло, що __eq__(), що викликається x == y, не є методом, що викликається набором. Що називається? Який ще метод я повинен визначити?

Примітка: Items повинні залишатися змінними і можуть змінюватися, тому я не можу надати __hash__()метод. Якщо це єдиний спосіб зробити це, то я перепишу для використання незмінних Items.


1
Була така сама проблема. Я припускаю, що ви маніпулюєте невеликими обсягами даних у коді. Це, мабуть, не є добрим кандидатом для використання бази даних. Я пам’ятаю, як міг створити набір і визначити функцію порівняння в C ++, і я також вірю в Java, однак, схоже, ви не можете зробити це з об’єктами словника в Python. Здається, хтось, можливо, написав бібліотеку "набір" на Python, яка може це зробити, але я не знаю про неї.
Кріс Датроу,

Відповіді:


32

Боюсь, вам доведеться запропонувати __hash__()метод. Але ви можете кодувати його так, щоб це не залежало від змінних атрибутів вашого Item.


3
< docs.python.org/reference/… > У другому абзаці тут вказується, що його __hash__()слід визначати лише для незмінних об’єктів.
Ада

1
@Nathanael: якщо об’єкт, можливо, доведеться змінити, ви можете зробити незмінну копію об’єкта, наприклад, frozenset () та set ().
Lie Ryan

2
@Nathanael - як ти хотів би зателефонувати __eq__? Порівняння цих (1,2) атрибутів? Тоді вам потрібно повернути трохи хешу (1,2) також у вашому __hash__методі.
eumiro

Або, оскільки foo і bar є незмінними, __hash __ () може повернути суму хешів foo і bar? Ні ... сума занадто недостовірна ... якби foo дорівнював 1, а такт 2, це було б рівно, як 1, а foo як 2, що неправильно. Яку математичну функцію можна використати для цієї мети? Модуль або поділ повинні працювати.
Ада

1
Натанаїл: Просто використовуйте hashвбудовану команду: hash((self.foo, self.bar)). Тут використовується хеш кортежу, який відповідає вашим потребам. (Ваші дані __eq__також можна записати для tupleпорівняння.)
Пі Дельпорт,

73

Так, вам потрібен __hash__()-метод І оператор порівняння, який ви вже надали.

class Item(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar
    def __repr__(self):
        return "Item(%s, %s)" % (self.foo, self.bar)
    def __eq__(self, other):
        if isinstance(other, Item):
            return ((self.foo == other.foo) and (self.bar == other.bar))
        else:
            return False
    def __ne__(self, other):
        return (not self.__eq__(other))
    def __hash__(self):
        return hash(self.__repr__())

3
На Python 3 вам не потрібно __ne__, і навіть у 2.x ви не повинні визначати __ne__терміни __eq__. Дивіться, stackoverflow.com/a/30676267/5337834
Джон Строуд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.