2009-08-04 4 views
147

Каков наилучший способ сравнить два экземпляра какого-либо объекта для равенства в Python? Я хотел бы быть в состоянии сделать что-то вродеСравнение экземпляров объектов для равенства по их атрибутам в Python

Пример:

doc1 = ErrorDocument(path='/folder',title='Page') 
doc2 = ErrorDocument(path='/folder',title='Page') 

if doc1 == doc2: # this should be True 
    #do something 

EDIT:

Для дальнейшего уточнения вопроса. Я хотел бы сравнить значения атрибутов и сделать более общее решение, чем

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

Если метод __eq__() выглядеть примерно так?

def __eq__(self, other): 
    # Is the other instance of the same object 
    # Loop through __dict__ and compare values to attributes of other 
+0

Вы почти получили его, но вам не нужно зацикливать другой __dict__, потому что Python может проверить равенство между встроенными типами. Проверьте мой ответ на простой фрагмент. –

ответ

216

Как обычно с Python, это KISS:

class Test(object): 

    def __init__(self, attr1, attr2): 
     self.attr1 = attr1 
     self.attr2 = attr2 

    def __str__(self): 
     return str(self.__dict__) 

    def __eq__(self, other): 
     return self.__dict__ == other.__dict__ 

t1 = Test("foo", 42) 
t2 = Test("foo", 42) 
t3 = Test("bar", 42) 

print t1, t2, t3 
print t1 == t2 
print t2 == t3 

Он выводит:

{'attr2': 42, 'attr1': 'foo'} {'attr2': 42, 'attr1': 'foo'} {'attr2': 42, 'attr1': 'bar'} 
True 
False 

NB: знать, что перед Python 3.0, вы более склонны использовать __cmp__ вместо __eq__, работая так же.

+16

Хороший ответ, но пробелы перед двоеточиями? Это PEP 8 no-no;) – Kiv

+55

LOL, да, я французский, мы пишем пробелы перед двоеточиями и раньше? а также ! слишком. Иногда я даже пишу комментарий по-французски, стыдно за меня :-) –

+7

Обратите внимание, что с помощью этой методики тестовый экземпляр также сравнится с любым другим экземпляром, словарь которого сравнивается. Вы также можете захотеть «isinstance (другое, self .__ class__)» в __eq__. –

2

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

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

Edit:

Как уже отмечалось, функция __cmp__ является устаревшим, начиная с версии 3.0. Вместо этого вы должны использовать методы “rich comparison”.

+0

Функция __cmp__ устарела для 3.0+ – Christopher

+1

Да, см. Http://stackoverflow.com/questions/1061283/style-why-lt-instead-of-cmp – Kiv

+0

А вы узнаете что-то новое каждый день, d – Silfverstrom

5

Внесите в свой класс метод __eq__; что-то вроде этого:

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

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

def __eq__(self, other): 
    return self.__dict__ == other.__dict__ 
+0

Возможно, вы имеете в виду 'self is other ', чтобы увидеть, являются ли они одним и тем же объектом. –

+2

-1. Даже если это два словаря, Python будет сравнивать их по ключам/значениям автоматически. Это не Java ... –

39

Вы переопределить rich comparison operators в вашем объекте.

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 
+2

Обратите внимание, что в Python 2.5 и далее класс должен определять '__eq __()', но только один из '__lt __()', '__le __()', '__gt __()' или ' __ge __() 'необходимо в дополнение к этому. Из этого Python может вывести другие методы. См. ['Functools'] (http://docs.python.org/2/library/functools.html) для получения дополнительной информации. – kba

+1

@kba, я не думаю, что это правда. Это может работать для модуля 'functools', но ** не работает ** для стандартных компараторов:' MyObj1! = Myobj2' будет работать, только если используется метод '__ne __()'. – Arel

+1

конкретным советом о functools должно быть использование декоратора '@ functools.total_ordering' на вашем классе, тогда, как указано выше, вы можете определить только' __eq__' и еще один, а остальное будет получено – Anentropic

-4

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

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

В вашем случае КСС (doc1, doc2) Она возвращает -1, если содержание мудр они одинаковы.

+1

-1, вы даже прочитали другие ответы? – Kiv

-1

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

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

+0

. По крайней мере, в Python 2.7 объекты по умолчанию сравниваются по идентификатору. Это означает, что CPython в практических словах сравнивает их адрес памяти. Вот почему cmp (o1, o2) возвращает 0 только тогда, когда «o1 является o2» и последовательно 1 или -1 в зависимости от значений id (o1) и id (o2) – yacc143

3

В качестве резюме:

  1. Советуют реализации __eq__, а не __cmp__, за исключением, если вы запустите питона < = 2.0 (__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) 
    
0

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

[i for i,j in 
zip([getattr(committed_vans_events[0][0].request, attr) 
     for attr in dir(committed_vans_events[0][0].request)], 
    [getattr(self.vans_states[0].onboard_passengers[0], attr) 
     for attr in dir(self.vans_states[0].onboard_passengers[0])]) 
if not i==j] 

дополнительное преимущество в том, что вы можете сжать его на одну строку и введите в окне «Вычислить выражение» при отладке в PyCharm.

+0

Я смеялся, а затем я плакал ... – Jerfov2

Смежные вопросы