terom@44: """ terom@44: Miscellaneous utilities terom@44: """ terom@44: terom@59: import functools terom@23: terom@96: class LazyProperty (object) : terom@12: """ terom@84: Lazy-loaded cached properties. terom@84: terom@84: This functions by overriding the class's property attribute with the actual value attribute in the instance's terom@84: dict, causing all future attribute lookups to return that directly. terom@96: terom@96: >>> class Foo (object) : terom@96: ... counter = 0 terom@96: ... @lazy_load terom@96: ... def foo (self) : terom@96: ... self.counter += 1 terom@96: ... return self.counter terom@96: ... terom@96: >>> foo = Foo(); foo.foo, foo.foo, foo.counter terom@96: (1, 1, 1) terom@96: terom@12: """ terom@12: terom@59: def __init__ (self, func) : terom@59: """ terom@59: Initialize with no value terom@59: """ terom@75: terom@84: # the getter function terom@75: self.func = func terom@84: terom@84: # the attribute name terom@75: self.name = func.__name__ terom@19: terom@73: def run (self, obj) : terom@59: """ terom@84: Run the background func and return the to-be-cached value. terom@84: terom@84: `obj` is the object instance terom@59: """ terom@28: terom@75: return self.func(obj) terom@73: terom@84: def store (self, obj, value) : terom@73: """ terom@84: Store the cached value, overring ourself terom@73: """ terom@96: terom@96: # print "[%x] %r.__dict__[%r] = %r" % (id(obj), obj, self.name, value) terom@96: terom@84: obj.__dict__[self.name] = value terom@73: terom@75: def __get__ (self, obj, owner) : terom@59: """ terom@84: Generate the value, store it as the attribute and return it. terom@59: """ terom@96: terom@84: # compute value terom@84: value = self.run(obj) terom@59: terom@84: # store it terom@84: self.store(obj, value) terom@84: terom@84: # return it terom@84: return value terom@96: terom@59: class LazyIteratorProperty (LazyProperty) : terom@59: """ terom@59: A lazy-loaded property that automatically converts an iterator/genexp into a list. terom@59: """ terom@59: terom@73: def run (self, obj) : terom@59: """ terom@59: Wrap LazyProperty.run to return a list terom@59: """ terom@59: terom@96: return list(self.func(obj)) terom@59: terom@59: lazy_load = LazyProperty terom@59: lazy_load_iter = LazyIteratorProperty terom@59: terom@96: # testing terom@96: if __name__ == '__main__' : terom@96: import doctest terom@96: terom@96: doctest.testmod() terom@96: