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.

 1#
 2#  Decorator that takes a list of members that are cached
 3#
 4def second_cache(members=None) :
 5    def inner(cls) :
 6        cls._second_cache_dirty = False
 7
 8        if members is None :
 9            def setter(self, name, value) :
10                if name not in ('_second_cache_dirty', ) :
11                    self._second_cache_dirty = True
12                return object.__setattr__(self, name, value)
13        else :
14            cls._second_cache_members = set(members)
15            def setter(self, name, value) :
16                if name in cls._second_cache_members and name not in ('_second_cache_dirty',):
17                    self._second_cache_dirty = True
18                return object.__setattr__(self, name, value)
19
20        cls.__setattr__ = setter
21
22        return cls
23    return inner
24
25#
26#  A revised property decorator that checkes the cacheable state of an object and
27#   calls refresh if defined and needed
28#
29def cache_prop(func) :
30    def call_me(self) :
31        if self._second_cache_dirty :
32            if hasattr(self, '_refresh') :
33                self._refresh()
34                self._second_cache_dirty = False
35        return func(self)
36    return property(call_me)
37
38#
39# Test class to demonstrate that only if the member bar is modified that the object
40#  calls the refresh method.
41#
42@second_cache(members=['bar'])
43class Test(object) :
44    def __init__(self) :
45        self.bar = 1
46        self.cat = 2
47        self.dog = 3
48        self.baz = 4
49
50    def _refresh(self) :
51        print "Cache Dirty - need to refresh object"
52
53    @cache_prop
54    def frog(self) :
55        return self.bar + self.cat
56
57    def fun(self) :
58        print "BigTop"
59
60t1 = Test()
61
62print "Dirty:", t1.frog
63t1.bar = 7
64print "Dirty:", t1.frog
65print "Clean:", t1.frog