2010-07-13 74 views
7

我想创建一个像属性一样工作的装饰器,只调用一次装饰函数,并且在随后的调用中总是返回第一个调用的结果。举例:如何为一个属性的延迟初始化创建装饰器

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? 

正如你所看到的,我不知道在哪里存储缓存值。最简单的解决方案似乎是维护一本字典,但我想知道是否有一个更优雅的解决方案。

编辑对不起,我忘记提及我希望属性是只读的。

+0

这可能是我的问题的重复:Python的懒财产装饰(http://stackoverflow.com/questions/3012421/python-lazy-property-decorator) – detly 2010-07-13 14:01:11

+0

你说得是。没有在建议框中看到它。投票结束。 – 2010-07-13 14:10:17

回答

14

Denis Otkidach's 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 ,那么下一次你 访问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 
+0

谢谢你。我刚刚编辑了我的问题以添加我忘记的要求。我怎样才能使这个只读? – 2010-07-13 13:50:30