如何为延迟初始化属性创建装饰器 [英] How to create decorator for lazy initialization of a property
问题描述
我想创建一个装饰器,其工作方式类似于属性,只是它仅调用装饰后的函数一次,并且在后续调用中始终返回第一个调用的结果。例如:
I want to create a decorator that works like a property, only it calls the decorated function only once, and on subsequent calls always return the result of the first call. An example:
def SomeClass(object):
@LazilyInitializedProperty
def foo(self):
print "Now initializing"
return 5
>>> x = SomeClass()
>>> x.foo
Now initializing
5
>>> x.foo
5
我的想法是为此编写一个自定义装饰器。所以我开始了,这就是我走了多远:
My idea was to write a custom decorator for this. So i started, and this is how far I came:
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?
我不知道将缓存的值存储在哪里。最简单的解决方案似乎只是维护字典,但我想知道是否对此有更优雅的解决方案。
As you can see, I do not know where to store the cached value. The simplest solution seems to be to just maintain a dictionary, but I am wondering if there is a more elegant solution for this.
编辑对不起为此,我忘了提到我希望该属性为只读。
EDIT Sorry for that, I forgot to mention that I want the property to be read-only.
推荐答案
Denis Otkidach的CachedAttribute 是一种方法修饰器,它使属性变得懒惰(计算一次,可访问)许多)。为了使其也成为只读,我添加了 __ set __
方法。为了保留重新计算的能力(请参阅下文),我添加了 __ delete __
方法:
Denis Otkidach's CachedAttribute is a method decorator which makes attributes lazy (computed once, accessible many). To make it also read-only, I added a __set__
method. To retain the ability to recalculate (see below) I added a __delete__
method:
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.bar
删除了'bar'
从 foo .__ dict __
,但属性
bar
仍保留在 Foo .__ dict __
。)
One of the beautiful things about CachedAttribute
(and
ReadOnlyCachedAttribute) is that if you del foo.bar
, then the next time you
access foo.bar
, the value is re-calculated. (This magic is made possible by
the fact that del foo.bar
removes 'bar'
from foo.__dict__
but the property
bar
remains in Foo.__dict__
.)
如果您不需要或不想重新计算此功能,则
然后是以下内容(基于 Mike Boers的lazyprop )是一种创建只读惰性属性的简单方法。 / p>
If you don't need or don't want this ability to recalculate, then the following (based on Mike Boers' lazyprop) is a simpler way to make a read-only lazy property.
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
这篇关于如何为延迟初始化属性创建装饰器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!