2010-07-13 73 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