Сравните экземпляры объектов на равенство по их атрибутам в Python - PullRequest
203 голосов
/ 04 августа 2009

У меня есть класс MyClass, который содержит две переменные-члены foo и bar:

class MyClass:
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

У меня есть два экземпляра этого класса, каждый из которых имеет идентичные значения для foo и bar:

x = MyClass('foo', 'bar')
y = MyClass('foo', 'bar')

Однако, когда я сравниваю их на равенство, Python возвращает False:

>>> x == y
False

Как я могу заставить python считать эти два объекта равными?

Ответы [ 8 ]

291 голосов
/ 04 августа 2009

Вам следует реализовать метод __eq__:

class MyClass:
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    def __eq__(self, other): 
        if not isinstance(other, MyClass):
            # don't attempt to compare against unrelated types
            return NotImplemented

        return self.foo == other.foo and self.bar == other.bar

Теперь выводит:

>>> x == y
True

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

Если вы моделируете неизменный тип, вы также должны реализовать хук datamodel __hash__:

class MyClass:
    ...

    def __hash__(self):
        # necessary for instances to behave sanely in dicts and sets.
        return hash((self.foo, self.bar))

Общее решение, такое как идея циклического перебора по __dict__ и сравнения значений, не рекомендуется - оно никогда не может быть по-настоящему общим, поскольку __dict__ может иметь несопоставимые или неустранимые типы, содержащиеся в.

N.B .: помните, что перед Python 3 вам может понадобиться использовать __cmp__ вместо __eq__. Пользователи Python 2 также могут захотеть внедрить __ne__, поскольку разумное поведение по умолчанию для неравенства (то есть инвертирование результата равенства) не будет автоматически создано в Python 2.

44 голосов
/ 04 августа 2009

Вы переопределяете операторы расширенного сравнения в вашем объекте.

class MyClass:
 def __lt__(self, other):
      # return comparison
 def __le__(self, other):
      # return comparison
 def __eq__(self, other):
      # return comparison
 def __ne__(self, other):
      # return comparison
 def __gt__(self, other):
      # return comparison
 def __ge__(self, other):
      # return comparison

Как это:

    def __eq__(self, other):
        return self._id == other._id
5 голосов
/ 04 августа 2009

Реализуйте метод __eq__ в своем классе; как то так:

def __eq__(self, other):
    return self.path == other.path and self.title == other.title

Редактировать: если вы хотите, чтобы ваши объекты сравнивались равными, если и только если они имеют одинаковые словари экземпляров:

def __eq__(self, other):
    return self.__dict__ == other.__dict__
4 голосов
/ 26 февраля 2016

Как итог:

  1. Рекомендуется использовать __eq__ вместо __cmp__, за исключением случаев, когда вы запускаете python <= 2.0 (<code>__eq__ был добавлен в 2.1)
  2. Не забудьте также реализовать __ne__ (должно быть что-то вроде return not self.__eq__(other) или return not self == other, за исключением очень особого случая)
  3. Не забывайте, что оператор должен быть реализован в каждом пользовательском классе, который вы хотите сравнить (см. Пример ниже).
  4. Если вы хотите сравнить с объектом, который может быть None, вы должны реализовать его. Переводчик не может угадать это ... (см. Пример ниже)

    class B(object):
      def __init__(self):
        self.name = "toto"
      def __eq__(self, other):
        if other is None:
          return False
        return self.name == other.name
    
    class A(object):
      def __init__(self):
        self.toto = "titi"
        self.b_inst = B()
      def __eq__(self, other):
        if other is None:
          return False
        return (self.toto, self.b_inst) == (other.toto, other.b_inst)
    
2 голосов
/ 04 августа 2009

При сравнении экземпляров объектов вызывается функция __cmp__.

Если по умолчанию у вас не работает оператор ==, вы всегда можете переопределить функцию __cmp__ для объекта.

Edit:

Как уже было отмечено, функция __cmp__ устарела с версии 3.0. Вместо этого вы должны использовать «богатое сравнение» методы.

0 голосов
/ 25 октября 2017

Если вы хотите получить сравнение атрибутов по атрибутам и посмотреть, если и где это не так, вы можете использовать следующее понимание списка:

[i for i,j in 
 zip([getattr(obj_1, attr) for attr in dir(obj_1)],
     [getattr(obj_2, attr) for attr in dir(obj_2)]) 
 if not i==j]

Дополнительным преимуществом здесь является то, что вы можете сжать его на одну строку и войти в окно «Evaluate Expression» при отладке в PyCharm.

0 голосов
/ 11 июня 2013

Я попробовал начальный пример (см. 7 выше), и он не работал в ipython. Обратите внимание, что cmp (obj1, obj2) возвращает «1» при реализации с использованием двух идентичных экземпляров объекта. Как ни странно, когда я изменяю одно из значений атрибута и перекомпоновываю, используя cmp (obj1, obj2) объект продолжает возвращать «1». (Вздох ...)

Хорошо, вам нужно перебрать два объекта и сравнить каждый атрибут с помощью знака ==.

0 голосов
/ 04 августа 2009

Экземпляр класса по сравнению с == становится неравным. Лучший способ - передать функцию cmp вашему классу, который все сделает.

Если вы хотите сделать сравнение по содержанию, вы можете просто использовать cmp (obj1, obj2)

В вашем случае cmp (doc1, doc2) вернет -1, если содержание будет одинаковым.

...