views:

78

answers:

2

Hi all

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!

+1  A: 

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)
Ned Batchelder
Awesome - I used this!!
rh0dium
+2  A: 

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)
Alex Martelli
Alex, I always enjoy studying your posts. Without a doubt your knowledge and willingness to share it is first-rate. Thanks
rh0dium
After reviewing your code — you are not completely doing what I'm after — so I've updated my learnings.
rh0dium
As I also commented on your edited Q, you'll still going crash in `__eq__` if some of the keys valid in `self` are missing from `other`, as I mentioned in my answer above. Also, your `__eq__` is not necessarily commutative (maybe a==b is true [[or false]] but b==a crashes... scary!_). So I'm still not clear on what you're trying to do. Maybe edit answer further to give a couple toy examples of person instances that should be equal vs different?
Alex Martelli