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.bar
удаляет 'bar'
из foo.__dict__
, но свойство
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