Сравните экземпляры объектов на равенство по их атрибутам
У меня есть класс 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" .
Ответ 6
В зависимости от вашего конкретного случая вы можете сделать:
>>> vars(x) == vars(y)
True
См. словарь Python из полей объекта
Ответ 7
Я написал это и поместил в модуль test/utils
в моем проекте. В тех случаях, когда это не класс, просто спланируйте его, это обойдет оба объекта и обеспечит
- каждый атрибут равен своему аналогу
- Не существует висячих атрибутов (атрибуты, которые существуют только для одного объекта)
Его большой... его не сексуально... но, о бой, он работает!
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, если контент будет таким же.