[Python-ideas] caching properties
George Sakkis
george.sakkis at gmail.com
Fri May 1 01:27:13 CEST 2009
On Thu, Apr 30, 2009 at 4:19 PM, spir <denis.spir at free.fr> wrote:
> (Aside from the hashable issue pointed by Arnaud)
> I wonder about having the whole parameter tuple as key for caching.
> In packrat parsing, you may have more than one parameter (including the source, indeed) but only one is relevant for memoizing (the position). Cache has to be reset anyway when starting a new parse, so that having the source in keys is irrelevant.
> Typically, if not a single value, I guess saved results form a simple array depending on an ordinal.
I think It's clear by now that caching in the general case is not
trivial, both in terms of API and implementation. That makes the
original request - caching properties only - more appealing since most
problems go away if there are no parameters.
In the simplest case where cache expiration is not supported,
cachedproperty can be a 7-line decorator:
def cachedproperty(fget):
def fget_wrapper(self):
try: return fget_wrapper._cached
except AttributeError:
fget_wrapper._cached = value = fget(self)
return value
return property(fget_wrapper, doc=fget.__doc__)
Cache expiration can be exposed by (ab)using the deleter; "del
obj.prop" looks much better than "ObjType.prop.fget.expire(obj)":
def cachedproperty(fget):
def fget_wrapper(self):
try: return fget_wrapper._cached
except AttributeError:
fget_wrapper._cached = value = fget(self)
return value
def fdel(self):
try: del fget_wrapper._cached
except AttributeError: pass
return property(fget_wrapper, fdel=fdel, doc=fget.__doc__)
And finally here's a general version that supports properly the new in
2.6 getter()/setter()/deleter() methods (note that setting a property
expires the cache, just like delete):
class cachedproperty(property):
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
if fget is not None:
def fget_wrapper(obj):
try: return self._cached
except AttributeError:
self._cached = value = fget(obj)
return value
else:
fget_wrapper = None
if fset is not None:
def fset_wrapper(obj, value):
fset(obj,value)
try: del self._cached
except AttributeError: pass
else:
fset_wrapper = None
if fdel is not None:
def fdel_wrapper(obj):
fdel(obj)
try: del self._cached
except AttributeError: pass
else:
def fdel_wrapper(obj):
try: del self._cached
except AttributeError: pass
super(cachedproperty,self).__init__(fget_wrapper,
fset_wrapper, fdel_wrapper, doc)
self.__doc__ = doc or getattr(fget, '__doc__', None)
# store for getter() / setter() / deleter()
self._fget, self._fset, self._fdel = fget, fset, fdel
def getter(self, getter):
return self.__class__(getter, self._fset, self._fdel, self.__doc__)
def setter(self, setter):
return self.__class__(self._fget, setter, self._fdel, self.__doc__)
def deleter(self, deleter):
return self.__class__(self._fget, self._fset, deleter, self.__doc__)
George
More information about the Python-ideas
mailing list