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

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

218
задан martineau 19 October 2019 в 00:11
поделиться

8 ответов

Вы должны реализовать метод __ 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 __ автоматически создаст экземпляры вашего класса unhashable, что означает, что они не могут храниться в наборах и dicts. Если вы не моделируете неизменяемый тип (например, если атрибуты 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: имейте в виду, что до Python 3 вам может потребоваться использовать __ cmp __ вместо __ eq __ . Пользователи Python 2 могут также захотеть реализовать __ ne __ , поскольку разумное поведение по умолчанию для неравенства (т.е. инвертирование результата равенства) не будет автоматически создано в Python 2.

316
ответ дан 23 November 2019 в 04:13
поделиться

В зависимости от Вашего конкретного случая Вы могли сделать:

>>> vars(x) == vars(y)
True

См. словарь Python от object' s поля

1
ответ дан 23 November 2019 в 04:13
поделиться

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

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

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

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

0
ответ дан 23 November 2019 в 04:13
поделиться

Необходимо реализовать метод __eq__:

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

      def __eq__(self,other):
           if not isinstance(other,MyClass):
                return NotImplemented
           else:
                #string lists of all method names and properties of each of these objects
                prop_names1 = list(self.__dict__)
                prop_names2 = list(other.__dict__)

                n = len(prop_names1) #number of properties
                for i in range(n):
                     if getattr(self,prop_names1[i]) != getattr(other,prop_names2[i]):
                          return False

                return True
0
ответ дан 23 November 2019 в 04:13
поделиться

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

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
46
ответ дан 23 November 2019 в 04:13
поделиться

Реализуйте метод __ eq __ в своем классе; что-то вроде этого:

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

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

def __eq__(self, other):
    return self.__dict__ == other.__dict__
5
ответ дан 23 November 2019 в 04:13
поделиться

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

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

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

-6
ответ дан 23 November 2019 в 04:13
поделиться

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

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

] Редактировать:

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

1
ответ дан 23 November 2019 в 04:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: