
On Thu, Apr 30, 2009 at 4:19 PM, spir <denis.spir@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