Python Lazy Object Reloader

I’m sure there’s a more general title for this kind of object, but the challenge came up and here’s what I put together.  The basic idea is to have a python class that only recomputes something hard when one of the attributes is modified.  It doesn’t make sense to recompute on every modification, nor does it make sense to recompute if it’s never read.

In all cases, here’s the code sample.  I’m sure it’ll be useful on stackoverflow someday.

#
#  Decorator that takes a list of members that are cached
#
def second_cache(members=None) :
    def inner(cls) :
        cls._second_cache_dirty = False

        if members is None :
            def setter(self, name, value) :
                if name not in ('_second_cache_dirty', ) :
                    self._second_cache_dirty = True
                return object.__setattr__(self, name, value)
        else :
            cls._second_cache_members = set(members)
            def setter(self, name, value) :
                if name in cls._second_cache_members and name not in ('_second_cache_dirty',):
                    self._second_cache_dirty = True
                return object.__setattr__(self, name, value)

        cls.__setattr__ = setter

        return cls
    return inner

#
#  A revised property decorator that checkes the cacheable state of an object and
#   calls refresh if defined and needed
#
def cache_prop(func) :
    def call_me(self) :
        if self._second_cache_dirty :
            if hasattr(self, '_refresh') :
                self._refresh()
                self._second_cache_dirty = False
        return func(self)
    return property(call_me)

#
# Test class to demonstrate that only if the member bar is modified that the object
#  calls the refresh method.
#
@second_cache(members=['bar'])
class Test(object) :
    def __init__(self) :
        self.bar = 1
        self.cat = 2
        self.dog = 3
        self.baz = 4

    def _refresh(self) :
        print "Cache Dirty - need to refresh object"

    @cache_prop
    def frog(self) :
        return self.bar + self.cat

    def fun(self) :
        print "BigTop"

t1 = Test()

print "Dirty:", t1.frog
t1.bar = 7
print "Dirty:", t1.frog
print "Clean:", t1.frog