Compare Object Instances for Equality by Their Attributes

Compare object instances for equality by their attributes

You should implement the method __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

Now it outputs:

>>> x == y
True

Note that implementing __eq__ will automatically make instances of your class unhashable, which means they can't be stored in sets and dicts. If you're not modelling an immutable type (i.e. if the attributes foo and bar may change the value within the lifetime of your object), then it's recommended to just leave your instances as unhashable.

If you are modelling an immutable type, you should also implement the data model hook __hash__:

class MyClass:
...

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

A general solution, like the idea of looping through __dict__ and comparing values, is not advisable - it can never be truly general because the __dict__ may have uncomparable or unhashable types contained within.

N.B.: be aware that before Python 3, you may need to use __cmp__ instead of __eq__. Python 2 users may also want to implement __ne__, since a sensible default behaviour for inequality (i.e. inverting the equality result) will not be automatically created in Python 2.

Most efficient way of comparing the contents of two class instances in python

Implement a __eq__ special method instead:

def __eq__(self, other, *attributes):
if not isinstance(other, type(self)):
return NotImplemented

if attributes:
d = float('NaN') # default that won't compare equal, even with itself
return all(self.__dict__.get(a, d) == other.__dict__.get(a, d) for a in attributes)

return self.__dict__ == other.__dict__

Now you can just use:

if newInstance in myList:

and Python will automatically use the __eq__ special method to test for equality.

In my version I retained the ability to pass in a limited set of attributes:

instance1.__eq__(instance2, 'attribute1', 'attribute2')

but using all() to make sure we only test as much as is needed.

Note that we return NotImplemented, a special singleton object to signal that the comparison is not supported; Python will ask the other object if it perhaps supports equality testing instead for that case.

How to compare two instances of an object Python?

The other answers are correct and provide the best way to do it, but I realized that you wrote:

print calls provided as part of an exercise: not my implementation

print(same_person(person1, person2))
print(same_person(person1, person3))

The exercise probably wants you to define a function outside the class. You can do that by removing that function from the class and writing it un-indented outside the class (without providing class type too). For example:

class Person: 
def __init__(self, name, age, id):
self.name = name
self.age = age
self.id = id

def same_person(lhs, rhs):
return lhs.id == rhs.id

person1 = Person("David Joyner", 30, 901234567)
person2 = Person("D. Joyner", 29, 901234567)
person3 = Person("David Joyner", 30, 903987654)

print(same_person(person1, person2))
print(same_person(person1, person3))

Equality of objects in Python

you have to tell python how exactly you want equality be defined.
do so by defining a special method __eq__ like this:

def __eq__(self, other):
return self.attrfoo == other.attrfoo # change that to your needs

the __cmp__(self, other) is the "old" style to compare instances of classes, and only used when there is no rich comparison special method found. read up on them here: http://docs.python.org/release/2.7/reference/datamodel.html#specialnames

Comparing object methods leads to different results in 3.8

What happened is this commit based on issue 16171610 (and this discussion on python-dev).

With these (selected) comments:

It seems unlogical to me that whether or not the instance methods of two different instances are equal or not depends on the equality of the instance.

followed by

All in all I think that this part was an accident
and never designed;

and

I think it makes little sense that the equality test for the instance
methods takes the equality of the instances into account. Imho, this
behaviour is inconsistent with the principle of no surprises. The
correct behaviour (again imho of course) is that instance methods only
compare equal to the same instance method of the same instance, where
'same instance' is based on 'is' not on '=='.

and

This change can be considered as a bugfix, but since it can break the user code (unlikely), it may be safer to merge it only in 3.8 and expose as a new feature.

So this seems to be considered a bugfix/feature because bound methods should only be equal if they are bound on the same instance, not if the instances are considered equal. In Python <= 3.7 the bound method equality calls the equivalent of instance1 == instance2 (thus calling your __eq__) of the instance while in Python 3.8 it checks if instance1 is instance2.


The corresponding changelog item can be found in section "Python 3.8.0 alpha 1" - it's a long list of items, so I included a copy here:

  • bpo-1617161: The hash of BuiltinMethodType instances (methods of built-in classes) now depends on the hash of the identity of __self__ instead of its value. The hash and equality of ModuleType and MethodWrapperType instances (methods of user-defined classes and some methods of built-in classes like str.__add__) now depend on the hash and equality of the identity of __self__ instead of its value. MethodWrapperType instances no longer support ordering.

Comparing two objects for varying set of properties

anyMatch

You can solve this with the Stream API, using anyMatch, with your rules basically being defined as Predicates.

For example checking if there is any person who is 20 years old:

List<Person> persons = ...

boolean doesAnyMatch = persons.stream()
.anyMatch(p -> p.getAge() == 20);

You can of course also setup the rule in a way that it compares with an existing item, mimicking equals a bit more:

p -> p.getAge() == otherPerson.getAge()


Predicate

You can setup all your rules somewhere else, as Predicates and then use them. For example:

List<Predicate<Person>> rules = List.of(
p -> p.getAge() == 20,
p -> p.getName().equals("John"),
p -> p.getAge() > 18,
p -> p.getName().length() > 10 && p.getAge() < 50
);

And then maybe use them in some sort of loop, whatever you need:

for (Predicate rule : rules) {
boolean doesAnyMatch = persons.stream()
.anyMatch(rule);
...
}


findAny

You can substitute anyMatch by a combination of filter and findAny to receive an actual match, i.e. a Person, instead of just a boolean:

Person matchingPerson = persons.stream()
.filter(rule)
.findAny();

Is there a way to check if two object contain the same values in each of their variables in python?

If you want the == to work, then implement the __eq__ method in your class to perform the rich comparison.

If all you want to do is compare the equality of all attributes, you can do that succinctly by comparison of __dict__ in each object:

class MyClass:

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


Related Topics



Leave a reply



Submit