degal/utils.py
author Tero Marttila <terom@fixme.fi>
Thu, 11 Jun 2009 22:50:21 +0300
changeset 96 d9cf1e272e90
parent 84 891545a38a2b
child 109 66a01c0806f1
permissions -rw-r--r--
fix LazyProperty to not inherit from property, so as to act as a non-data descriptor, which can then be overriden by per-instance __dict__ values
"""
    Miscellaneous utilities
"""

import functools

class LazyProperty (object) :
    """
        Lazy-loaded cached properties.

        This functions by overriding the class's property attribute with the actual value attribute in the instance's
        dict, causing all future attribute lookups to return that directly.

        >>> class Foo (object) :
        ...     counter = 0
        ...     @lazy_load
        ...     def foo (self) :
        ...             self.counter += 1
        ...             return self.counter
        ... 
        >>> foo = Foo(); foo.foo, foo.foo, foo.counter
        (1, 1, 1)

    """

    def __init__ (self, func) :
        """
            Initialize with no value
        """
        
        # the getter function
        self.func = func

        # the attribute name
        self.name = func.__name__

    def run (self, obj) :
        """
            Run the background func and return the to-be-cached value.

            `obj` is the object instance
        """

        return self.func(obj)
 
    def store (self, obj, value) :
        """
            Store the cached value, overring ourself
        """

#        print "[%x] %r.__dict__[%r] = %r" % (id(obj), obj, self.name, value)

        obj.__dict__[self.name] = value

    def __get__ (self, obj, owner) :
        """
            Generate the value, store it as the attribute and return it.
        """

        # compute value
        value = self.run(obj)

        # store it
        self.store(obj, value)
        
        # return it
        return value
    
class LazyIteratorProperty (LazyProperty) :
    """
        A lazy-loaded property that automatically converts an iterator/genexp into a list.
    """

    def run (self, obj) :
        """
            Wrap LazyProperty.run to return a list
        """

        return list(self.func(obj))

lazy_load = LazyProperty
lazy_load_iter = LazyIteratorProperty

# testing
if __name__ == '__main__' :
    import doctest

    doctest.testmod()