Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm looking for the most efficient way of comparing the contents of two class instances. I have a list containing these class instances, and before appending to the list I want to determine if their property values are the same. This may seem trivial to most, but after perusing these forums I wasn't able specific to what I'm trying to do. Also note that I don't have an programming background.

This is what I have so far:

class BaseObject(object):
    def __init__(self, name=''):
        self._name = name


    def __repr__(self):
        return '<{0}: \'{1}\'>'.format(self.__class__.__name__, self.name)

    def _compare(self, other, *attributes):
        count = 0
        if isinstance(other, self.__class__):
            if len(attributes):
                for attrib in attributes:
                    if (attrib in self.__dict__.keys()) and (attrib in other.__dict__.keys()):
                        if self.__dict__[attrib] == other.__dict__[attrib]:
                            count += 1
                return (count == len(attributes))
            else:
                for attrib in self.__dict__.keys():
                    if (attrib in self.__dict__.keys()) and (attrib in other.__dict__.keys()):
                        if self.__dict__[attrib] == other.__dict__[attrib]:
                            count += 1
                return (count == len(self.__dict__.keys()))
    def _copy(self):
        return (copy.deepcopy(self))

Before adding to my list, I'd do something like:

found = False
for instance in myList:
    if instance._compare(newInstance): 
        found = True
        Break

if not found: myList.append(newInstance)

However I'm unclear whether this is the most efficient or python-ic way of comparing the contents of instances of the same class.

like image 685
Klaudikus Avatar asked Jan 28 '26 02:01

Klaudikus


1 Answers

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.

like image 67
Martijn Pieters Avatar answered Jan 29 '26 16:01

Martijn Pieters