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 ofModuleType
andMethodWrapperType
instances (methods of user-defined classes and some methods of built-in classes likestr.__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 Predicate
s.
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 Predicate
s 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
Split a String by Spaces -- Preserving Quoted Substrings -- in Python
Activate a Virtualenv with a Python Script
How to Convert Each Item in the List to String, for the Purpose of Joining Them
How to Filter a Dictionary According to an Arbitrary Condition Function
How to Capitalize the First Letter of Each Word in a String
Update Value of a Nested Dictionary of Varying Depth
How to Access the Ith Column of a Numpy Multidimensional Array
Writing a Connection String When Password Contains Special Characters
How to Read Text from the Clipboard
Change One Value Based on Another Value in Pandas
Splitting a List into N Parts of Approximately Equal Length
The Modulo Operation on Negative Numbers in Python
How to Save a New Sheet in an Existing Excel File, Using Pandas
How Would You Make a Comma-Separated String from a List of Strings
How to Test That a Python Function Throws an Exception
How to Split a String of Space Separated Numbers into Integers