Creating an object that can track when its attributes are modified

Ben Sizer kylotan at gmail.com
Wed Mar 6 17:07:52 CET 2013


I am trying to make an object that can track when its attributes have been assigned new values, and which can rollback to previous values where necessary. I have the following code which I believe works, but would like to know if there are simpler ways to achieve this goal, or if there are any bugs I haven't seen yet.


class ChangeTrackingObject(object):
    def __init__(self):
        self.clean()

    def clean(self):
        """Mark all attributes as unmodified."""
        object.__setattr__(self, '_dirty_attributes', dict())

    def dirty_vals(self):
        """Returns all dirty values."""
        return dict( [ (k,v) for k,v in self.__dict__.iteritems() if k in self._dirty_attributes]  )

    def get_changes_and_clean(self):
        """Helper that collects all the changes and returns them, cleaning the dirty flags at the same time."""
        changes = self.dirty_vals()
        self.clean()
        return changes

    def rollback(self):
        """Reset attributes to their previous values."""
        for k,v in self._dirty_attributes.iteritems():
            object.__setattr__(self, k, v)
        self.clean()

    def __setattr__(self, key, value):
        # If the first modification to this attribute, store the old value
        if key not in self._dirty_attributes:
            if key in self.__dict__:
                self._dirty_attributes[key] = object.__getattribute__(self, key)
            else:
                self._dirty_attributes[key] = None
        # Set the new value
        object.__setattr__(self, key, value)


I am aware that adding a new attribute and then calling rollback() leaves the new attribute in place with a None value - maybe I can use a special DeleteMe marker object in the _dirty_attributes dict along with a loop that calls delattr on any attribute that has that value after a rollback.

I also believe that this won't catch modification to existing attributes as opposed to assignments: eg. if one of the attributes is a list and I append to it, this system won't notice. Is that something I can rectify easily?

Any other comments or suggestions?

Thanks,
-- 
Ben Sizer



More information about the Python-list mailing list