wonderbeyond
8/19/2015 - 8:45 AM

lazy property implementation in python

lazy property implementation in python

"""
See also: sqlalchemy.util.langhelpers.memoized_property
"""


# Property name to hold all lazy data
_data_holder_attr = '_lazy_properties'

def clean_lazy_proterties(instance):
    '''Clean all lazy properties'''
    setattr(instance, _data_holder_attr, {})
    
class lazy_property(object):
    """lazy property decorator, just like built-in `property`"""
    def __init__(self, fget):
        self.fget = fget
        self.property_name = fget.__name__
        
    def get_lazy_data(self, instance):
        if not hasattr(instance, _data_holder_attr):
            setattr(instance, _data_holder_attr, {})
        return getattr(instance, _data_holder_attr)

    def __get__(self, instance, owner):
        if instance is None:
            return None
        
        lazy_data = self.get_lazy_data(instance)
        
        if self.property_name in lazy_data:
            return lazy_data[self.property_name]
            
        value = self.fget(instance)
        lazy_data[self.property_name] = value
        return value

if __name__ == '__main__':
    class Test(object):
        '''Test case for lazy_property'''
        @lazy_property
        def somenum(self):
            import random
            return random.randrange(0, 1000)

    t = Test()
    print(t.somenum, t.somenum, t.somenum)  # print a random number three times
    clean_lazy_proterties(t)
    print(t.somenum, t.somenum, t.somenum)  # print another random number three times
"""
See: https://segmentfault.com/a/1190000005818249

(A better implementation: django.utils.functional.cached_property)

Usage:

    class Circle(object):
        def __init__(self, radius):
            self.radius = radius

        @lazy_property
        def area(self):
            print 'evalute'
            return 3.14 * self.radius**2
"""

class lazy_property(object):
    def __init__(self, fget):
        self.fget = fget

    def __get__(self, instance, cls):
        value = self.fget(instance)
        setattr(instance, self.fget.__name__, value)
        return value