rh0dium
rh0dium

Reputation: 7052

Class Objects and comparing specific attributes

I have the following code.

class person(object):

    def __init__(self, keys):
        for item in keys:
            setattr(self, item, None)

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

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

Now I want to take this code and only do __eq__ on a specific set of attrs ("keys"). So I changed it to do this:

class person(object):

    def __init__(self, keys):
        self.valid_keys = keys
        for item in keys:
            setattr(self, item, None)

    def __str__(self):
        return dict([(i, getattr(self, i)) for i in self.valid_keys ])

    def __eq__(self, other) :
        assert isinstance(other, person)
        self_vals = [ getattr(self, i) for i in self.valid_keys ]
        other_vals = [ getattr(other, i) for i in self.valid_keys ]
        return self_vals == other_vals

I have read the following two awesome posts (here and here) and my fundamental question is:

Is this the right approach or is there a better way to do this in python?

Obviously TMTOWTDI - but I'd like to keep and follow a standard pythonic approach. Thanks!!

Updates

I was asked why do I not fix the attrs in my class. This is a great question and here's why. The purpose of this is to take several dis-jointed employee records and build a complete picture of an employee. For example I get my data from ldap, lotus notes, unix passwd files, bugzilla data, etc. Each of those has uniq attrs and so I generalized them into a person. This gives me a quick consistent way to compare old records to new records. HTH. Thanks

** Updates Pt.2 **

Here is what I ended up with:

class personObj(object):

    def __init__(self, keys):
        self.__dict__ = dict.fromkeys(keys)
        self.valid_keys = keys

    def __str__(self):
        return str([(i, getattr(self, i)) for i in self.valid_keys ])

    def __eq__(self, other):
        return isinstance(other, personObj) and all(getattr(self, i) == getattr(other, i) for i in self.valid_keys)

Thanks to both gents for reviewing!

Upvotes: 0

Views: 174

Answers (2)

Alex Martelli
Alex Martelli

Reputation: 882691

There are minor enhancements (bug fixes) I'd definitely do.

In particular, getattr called with two arguments raises an ArgumentError if the attribute's not present, so you could get that exception if you were comparing two instances with different keys. You could just call it with three args instead (the third one is returned as the default value when the attribute is not present) -- just don't use None as the third arg in this case since it's what you normally have as the value (use a sentinel value as the third arg).

__str__ is not allowed to return a dict: it must return a string.

__eq__ between non-comparable objects should not raise -- it should return False.

Bugs apart, you can get the object's state very compactly with self.__dict__, or more elegantly with vars(self) (you can't reassign the whole dict with the latter syntax, though). This bit of knowledge lets you redo your class entirely, in a higher-level-of-abstraction way -- more compact and expeditious:

class person(object):

    def __init__(self, keys):
        self.__dict__ = dict.fromkeys(keys)

    def __str__(self):
        return str(vars(self))

    def __eq__(self, other):
        return isinstance(other, person) and vars(self) == vars(other)

Upvotes: 2

Ned Batchelder
Ned Batchelder

Reputation: 376012

You can simplify your comparison from:

self_vals = [ getattr(self, i) for i in self.valid_keys ]
other_vals = [ getattr(other, i) for i in self.valid_keys ]
return self_vals == other_vals

to:

return all(getattr(self, i) == getattr(other, i) for i in self.valid_keys)

Upvotes: 1

Related Questions