Сравните экземпляры объектов на равенство по их атрибутам

У меня есть класс 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 считать эти два объекта равными?

Ответ 1

Вы должны реализовать метод __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 могут изменить значение в течение времени жизни вашего объекта), то рекомендуется просто оставить ваши экземпляры как не подлежащие изменению.

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

class MyClass:
    ...

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

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

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

Ответ 2

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

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

Ответ 3

Внедрить метод __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

Как резюме:

  • Рекомендуется реализовать __eq__, а не __cmp__, за исключением того, что вы запустили python <= 2.0 (__eq__ был добавлен в 2.1)
  • Не забудьте также реализовать __ne__ (должно быть что-то вроде return not self.__eq__(other) или return not self == other, за исключением особого случая)
  • Не забывайте, что оператор должен быть реализован в каждом настраиваемом классе, который вы хотите сравнить (см. пример ниже).
  • Если вы хотите сравнить с объектом, который может быть 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)
    

Ответ 5

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

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

Edit:

Как уже отмечалось, функция __cmp__ устарела с 3,0. Вместо этого вы должны использовать методы "rich compare" .

Ответ 7

Я написал это и поместил в модуль test/utils в моем проекте. В тех случаях, когда это не класс, просто спланируйте его, это обойдет оба объекта и обеспечит

  1. каждый атрибут равен своему аналогу
  2. Не существует висячих атрибутов (атрибуты, которые существуют только для одного объекта)

Его большой... его не сексуально... но, о бой, он работает!

def assertObjectsEqual(obj_a, obj_b):

    def _assert(a, b):
        if a == b:
            return
        raise AssertionError(f'{a} !== {b} inside assertObjectsEqual')

    def _check(a, b):
        if a is None or b is None:
            _assert(a, b)
        for k,v in a.items():
            if isinstance(v, dict):
                assertObjectsEqual(v, b[k])
            else:
                _assert(v, b[k])

    # Asserting both directions is more work
    # but it ensures no dangling values on
    # on either object
    _check(obj_a, obj_b)
    _check(obj_b, obj_a)

Вы можете немного почистить его, удалив _assert и просто используя обычный старый assert, но тогда сообщение, которое вы получаете, когда оно выходит из строя, очень бесполезно.

Ответ 8

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

[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.

Ответ 9

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

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

Ответ 10

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

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

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