繁体   English   中英

如何为属性的延迟初始化创建装饰器

[英]How to create decorator for lazy initialization of a property

我想创建一个像属性一样工作的装饰器,只调用一次装饰函数,后续调用总是返回第一次调用的结果。 一个例子:

def SomeClass(object):
    @LazilyInitializedProperty
    def foo(self):
        print "Now initializing"
        return 5

>>> x = SomeClass()
>>> x.foo
Now initializing
5
>>> x.foo
5

我的想法是为此编写一个自定义装饰器。 所以我开始了,这就是我走了多远:

class LazilyInitializedProperty(object):
    def __init__(self, function):
        self._function = function

    def __set__(self, obj, value):
        raise AttributeError("This property is read-only")

    def __get__(self, obj, type):
        # problem: where to store the value once we have calculated it?

如您所见,我不知道存储缓存值的位置。 最简单的解决方案似乎只是维护字典,但我想知道是否有更优雅的解决方案。

编辑很抱歉,我忘了提到我希望该房产是只读的。

Denis Otkidach的CachedAttribute是一个方法装饰器,它使属性变得懒惰(计算一次,可访问许多)。 为了使它也是只读的,我添加了一个__set__方法。 为了保留重新计算的能力(见下文),我添加了一个__delete__方法:

class ReadOnlyCachedAttribute(object):    
    '''Computes attribute value and caches it in the instance.
    Source: Python Cookbook 
    Author: Denis Otkidach https://stackoverflow.com/users/168352/denis-otkidach
    This decorator allows you to create a property which can be computed once and
    accessed many times. Sort of like memoization
    '''
    def __init__(self, method, name=None):
        self.method = method
        self.name = name or method.__name__
        self.__doc__ = method.__doc__
    def __get__(self, inst, cls): 
        if inst is None:
            return self
        elif self.name in inst.__dict__:
            return inst.__dict__[self.name]
        else:
            result = self.method(inst)
            inst.__dict__[self.name]=result
            return result    
    def __set__(self, inst, value):
        raise AttributeError("This property is read-only")
    def __delete__(self,inst):
        del inst.__dict__[self.name]

例如:

if __name__=='__main__':
    class Foo(object):
        @ReadOnlyCachedAttribute
        # @read_only_lazyprop
        def bar(self):
            print 'Calculating self.bar'  
            return 42
    foo=Foo()
    print(foo.bar)
    # Calculating self.bar
    # 42
    print(foo.bar)    
    # 42
    try:
        foo.bar=1
    except AttributeError as err:
        print(err)
        # This property is read-only
    del(foo.bar)
    print(foo.bar)
    # Calculating self.bar
    # 42

关于CachedAttribute (和ReadOnlyCachedAttribute)的一个del foo.bar是,如果你是del foo.bar ,那么下次访问foo.bar ,会重新计算该值。 (这个魔法可以通过del foo.barfoo.__dict__删除'bar' ,但属性bar保留在Foo.__dict__ 。)

如果您不需要或不希望此功能重新计算,则以下(基于Mike Boers的lazyprop )是一种更简单的方法来创建只读的惰性属性。

def read_only_lazyprop(fn):
    attr_name = '_lazy_' + fn.__name__
    @property
    def _lazyprop(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)
    @_lazyprop.setter
    def _lazyprop(self,value):
        raise AttributeError("This property is read-only")
    return _lazyprop

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM