
I think it would be nice to add a "cache" argument to the property() constructor. When "cache" was True, property would only ask the getter function once for the result. This would simplify properties that require expensive operations to compute.

On Wed, Apr 29, 2009 at 4:23 PM, Benjamin Peterson <benjamin@python.org> wrote:
http://en.wikipedia.org/wiki/Eiffel_(programming_language)#Once_routines Sounds pretty much like what you're suggesting. Cheers, Chris -- http://blog.rebertia.com

On Wed, Apr 29, 2009 at 10:33:27PM +0000, Benjamin Peterson wrote:
http://ppa.cvs.sourceforge.net/viewvc/*checkout*/ppa/QPS/qps/qUtils.py See class CachedAttribute. Just use @CachedAttribute instead of @property. Oleg. -- Oleg Broytmann http://phd.pp.ru/ phd@phd.pp.ru Programmers don't die, they just GOSUB without RETURN.

On 29 Apr 2009, at 15:33, Benjamin Peterson wrote:
You know, I have been wishing for something like this as well. I contribute from time to time to the SCons project (a Python-based build system), and caching values of things is crucial to its performance. I had hoped to use a pattern like this (which doesnt work): class Foo(object): def _get_path(self): # Compute it path = do_something_to_compute_path() # Replace the property with the value itself, all future lookups get this value immediately object.__setattr__(self, 'path', path) # (note: this is illegal and doesnt actually work) return path path = property(_get_path) def bar(self): # For whatever reason, this operation invalidated the cached value, so we reset it. # If and only if queried again is the new value created object.__setattr__(self, 'path', property(_get_path)) This would replace the way that SCons currently does it (doing this from memory, but the gist is right): def _old_way_get_path(self): try: cache = self._cache return cache['path'] except KeyError: path = cache['path'] = do_something_to_compute_path() return path def old_bar(self): # invalidate the value: del self._cache['path'] The old way works, but every cached call "obj.path" requires three dict lookups (someobj.path, self._cache, and cache['path']), whereas the first pattern requires exactly one dict lookup for the cached value (someobj.path). I would definitely like to see some mechanism (not necessarily how I imagined it) for supporting cached value lookups efficiently. I am curious to see how this discussion goes. Jared

On Thu, 30 Apr 2009 08:33:27 am Benjamin Peterson wrote:
-1 on an extra parameter to property. +1 on a cache decorator. It is clear and simple enough to write something like: @property @cache # or "once" if you prefer the Eiffel name def value(self): pass Such a decorator could then be used on any appropriate function, not just for properties. -- Steven D'Aprano

I think one thing that should be addressed is how to change the cached value, for example after the setter is called. Having multiple decorators would seem to make this slightly more complicated. If we were using only one, it would be easy to just add a cache property to the decorated function, which the setter could easily deal with. @property(cache=True) def foo(self): return(self.bar * 99) @foo.setter def foo(self, bar): self.bar = bar / 99 del foo.cache # or foo.cache = bar I guess this doesn't actually work anyway because of the way setters are specified, since it would refer to the setter instead of the property. Hmm. Perhaps, since the return value of setters is currently meaingless, it could be written into the cache, or used to specify if the cache should be cleared? Or just clear the cache automatically any time the setter is called. I realize that these ideas would probably introduce excessive overhead and have some other problems, but I think this is something that needs to be figured out before too long, so having some more ideas out there probably won't hurt. - JB On 2009-04-30, spir <denis.spir@free.fr> wrote:

On Thu, 30 Apr 2009 10:36:17 pm Jeremy Banks wrote:
I think one thing that should be addressed is how to change the cached value, for example after the setter is called.
For the use-case being discussed, you generally don't want to cache a value that changes. You generally want to cache a value which is expensive to calculate, but never changes. However, there are uses for caches that expire after some time. I just can't think of any where I'd want them to be accessed via a property instead of a function call.
Having multiple decorators would seem to make this slightly more complicated.
I don't see why. The cache decorator could expose an interface to expire the cached value. Here's a quick&dirty but working solution. (Tested under Python 2.5.) def once(func): class Cache(object): def __call__(self, *args, **kwargs): try: return self._value except AttributeError: result = func(*args, **kwargs) self._value = result return result def expire(self): del self._value return Cache() class Test(object): @property @once def expensive(self): import time time.sleep(20) return 1 Works like a charm :) (At least for a solution I knocked up in 30 seconds.) The only downside is that to expire the cache, you need the not-very-obvious call: Test.expensive.fget.expire() rather than t.expensive.expire(), which can't work for obvious reasons.
I'm not exactly sure that a solution that doesn't work can be said to "easily deal with" anything :) -- Steven D'Aprano

Steven D'Aprano wrote:
This is slightly better (name change as in Antoine Pitrou's comment): class cached(object): def __init__(self, function): self._function = function self._cache = {} def __call__(self, *args): try: return self._cache[args] except KeyError: self._cache[args] = self._function(*args) return self._cache[args] def expire(self, *args): del self._cache[args]
Similarly, I can use: class Test(object): @property @cached def expensive(self): import time time.sleep(20) return 1 t = Test() and use: t.expensive
I'll bet you cannot quite do that; I need to do: Test.expensive.fget.expire(t) Note that 'cached' can do multi-arg functions, though it doesn't handle kwargs, since there is some ambiguity between calling keyword-provided args and the args vector. The change to cache redundant uses would be something like: - def __call__(self, *args): + def __call__(self, *args, **kwargs): + args = args, tuple(sorted(kwargs.items())): - def expire(self, *args): + def expire(self, *args, **kwargs)): + args = args, tuple(sorted(kwargs.items())): However, this might be fairly counter-intuitive (specifically when expiring some values, and thinking you have them all). --Scott David Daniels Scott.Daniels@Acm.Org

Le Thu, 30 Apr 2009 12:22:48 -0700, Scott David Daniels <Scott.Daniels@Acm.Org> s'exprima ainsi:
(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. Denis ------ la vita e estrany

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

George Sakkis <george.sakkis@gmail.com> writes:
Nevertheless, the “memoize” pattern *is* well-understood, and already implemented for Python as discussed earlier. It covers the “cached property” as a degenerate (i.e. simpler) case. Making a specific decorator that *only* addresses “cached property” is too narrow, IMO. I think a “memoize” decorator (by whatever name) is the right level of generality to address this use case, and has the advantage of existing implementations. -- \ “As I bit into the nectarine, it had a crisp juiciness about it | `\ that was very pleasurable - until I realized it wasn't a | _o__) nectarine at all, but A HUMAN HEAD!!” —Jack Handey | Ben Finney

On Thu, Apr 30, 2009 at 7:37 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
IMHO no existing implementation is good enough for addition to the standard library. Off the top of my head, some of the issues a general and robust implementation should address are: - Non hashable parameters. - Caching keys based on a subset of the provided parameters (or perhaps more general a key function). - Bounded-length caches. - Different expiration policies for bounded-length caches. - As good performance as possible given the previous constraints. Of course one can write a vanilla version in a few lines, and indeed searching for "memoize" in code.activestate.com returns no less than 125 results (including one I posted 4.5 years ago). I think the sheer number of the posted recipes is an indication that none of them has "solved" the problem for good, otherwise people would just reuse it instead of keep posting alternatives. George

On Thu, Apr 30, 2009, George Sakkis wrote:
Then a full-blown PEP is required. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "If you think it's expensive to hire a professional to do the job, wait until you hire an amateur." --Red Adair

On Fri, 1 May 2009 10:16:11 am George Sakkis wrote:
Or people just like re-inventing the wheel. By all means go ahead and write a PEP and develop an heavyweight "deal with everything including the kitchen sink" solution. But in the meantime, the standard library could do with a couple of nice, simple, lightweight memoize decorators. -- Steven D'Aprano

On Fri, 1 May 2009 09:27:13 am George Sakkis wrote:
In my opinion, properties aren't special enough to need a special cachedproperty() decorator. Especially not get-only properties that never expire, since they're just a function taking a single argument that returns a constant result. A generic cache that runs on any function should be sufficient, and more useful than one that is specific to properties. Properties are not the only application for caching expensive function calls that never change. -- Steven D'Aprano

On 30 Apr 2009, at 12:22, Scott David Daniels wrote:
The only thing I dislike is how many dictionary lookups are required in order to return the value after it's been cached. I count 4 lookups (object.prop, prop.__call__, self._cache, and self._cache[args]). These add up, especially if object.prop could have returned the value immediately without having to go through so much indirection (but this is not currently possible) Jared

On Fri, 1 May 2009 06:59:55 am Jared Grubb wrote:
But you shouldn't be comparing a cheap attribute requiring one key lookup to a cache requiring four lookups. You should be comparing an expensive function call to four lookups. If the function isn't expensive, there's no value in caching it. -- Steven D'Aprano

Steven D'Aprano <steve@...> writes:
-1 on an extra parameter to property.
+1 on a cache decorator.
+1 for calling it "cached" or "memoize". I don't know if it has its place in the builtin namespace, though. Putting it in functools would be fine. (it may need a way to flush/invalidate the cache, too: @cached def foo(l=[4]): l[0] += 1 return 4 print foo() # 5 print foo() # 5 foo.flush() print foo() # 6 )

I agree, -1 on modifying @property and +1 on adding a new decorator to functools or some other module, maybe a collection of useful decorators. Cheers, Daniel -- Psss, psss, put it down! - http://www.cafepress.com/putitdown

Le Thu, 30 Apr 2009 05:55:41 -0700, Daniel Fetchinson <fetchinson@googlemail.com> s'exprima ainsi:
While I regularly use caching/memoizing (e.g. for packrat parsing) and I like the idea of having it predefined in the language, now I wonder whether it's really worth it. Because usually it's a very simple thing to implement -- often two obvious lines of code -- and easy to understand, even for someone who does not know the principle yet. E.g for a property getter: def _getThing(...): if self._thing is not None: return self._thing <else compute thing> (Also note that for a func you can cache on the func itself: func.cache -- but the 2-3 cases when I thought at that, it was rather a design issue with global vars.) Maybe I'm missing the point? Denis ------ la vita e estrany

spir <denis.spir@...> writes:
now I wonder whether it's really worth it. Because usually it's a very simple thing to implement -- often two obvious lines of code -- and easy to
understand, even for someone who does
not know the principle yet.
Well, for one, a generic implementation may have to be thread-safe. Also, while it's easy to implement, it's the kind of useful primitive - like enumerate() and others - which is nice to have builtin in the language or the standard library. Regards Antoine.

Benjamin Peterson <benjamin@python.org> writes:
I would prefer this as a decorator (not affecting the function signature), and applicable to any function (not just a property). This is the “memoize” pattern, implemented as a decorator in <URL:http://code.activestate.com/recipes/496879/>. -- \ Lucifer: “Just sign the Contract, sir, and the Piano is yours.” | `\ Ray: “Sheesh! This is long! Mind if I sign it now and read it | _o__) later?” —http://www.achewood.com/ | Ben Finney

On Wed, Apr 29, 2009 at 4:23 PM, Benjamin Peterson <benjamin@python.org> wrote:
http://en.wikipedia.org/wiki/Eiffel_(programming_language)#Once_routines Sounds pretty much like what you're suggesting. Cheers, Chris -- http://blog.rebertia.com

On Wed, Apr 29, 2009 at 10:33:27PM +0000, Benjamin Peterson wrote:
http://ppa.cvs.sourceforge.net/viewvc/*checkout*/ppa/QPS/qps/qUtils.py See class CachedAttribute. Just use @CachedAttribute instead of @property. Oleg. -- Oleg Broytmann http://phd.pp.ru/ phd@phd.pp.ru Programmers don't die, they just GOSUB without RETURN.

On 29 Apr 2009, at 15:33, Benjamin Peterson wrote:
You know, I have been wishing for something like this as well. I contribute from time to time to the SCons project (a Python-based build system), and caching values of things is crucial to its performance. I had hoped to use a pattern like this (which doesnt work): class Foo(object): def _get_path(self): # Compute it path = do_something_to_compute_path() # Replace the property with the value itself, all future lookups get this value immediately object.__setattr__(self, 'path', path) # (note: this is illegal and doesnt actually work) return path path = property(_get_path) def bar(self): # For whatever reason, this operation invalidated the cached value, so we reset it. # If and only if queried again is the new value created object.__setattr__(self, 'path', property(_get_path)) This would replace the way that SCons currently does it (doing this from memory, but the gist is right): def _old_way_get_path(self): try: cache = self._cache return cache['path'] except KeyError: path = cache['path'] = do_something_to_compute_path() return path def old_bar(self): # invalidate the value: del self._cache['path'] The old way works, but every cached call "obj.path" requires three dict lookups (someobj.path, self._cache, and cache['path']), whereas the first pattern requires exactly one dict lookup for the cached value (someobj.path). I would definitely like to see some mechanism (not necessarily how I imagined it) for supporting cached value lookups efficiently. I am curious to see how this discussion goes. Jared

On Thu, 30 Apr 2009 08:33:27 am Benjamin Peterson wrote:
-1 on an extra parameter to property. +1 on a cache decorator. It is clear and simple enough to write something like: @property @cache # or "once" if you prefer the Eiffel name def value(self): pass Such a decorator could then be used on any appropriate function, not just for properties. -- Steven D'Aprano

I think one thing that should be addressed is how to change the cached value, for example after the setter is called. Having multiple decorators would seem to make this slightly more complicated. If we were using only one, it would be easy to just add a cache property to the decorated function, which the setter could easily deal with. @property(cache=True) def foo(self): return(self.bar * 99) @foo.setter def foo(self, bar): self.bar = bar / 99 del foo.cache # or foo.cache = bar I guess this doesn't actually work anyway because of the way setters are specified, since it would refer to the setter instead of the property. Hmm. Perhaps, since the return value of setters is currently meaingless, it could be written into the cache, or used to specify if the cache should be cleared? Or just clear the cache automatically any time the setter is called. I realize that these ideas would probably introduce excessive overhead and have some other problems, but I think this is something that needs to be figured out before too long, so having some more ideas out there probably won't hurt. - JB On 2009-04-30, spir <denis.spir@free.fr> wrote:

On Thu, 30 Apr 2009 10:36:17 pm Jeremy Banks wrote:
I think one thing that should be addressed is how to change the cached value, for example after the setter is called.
For the use-case being discussed, you generally don't want to cache a value that changes. You generally want to cache a value which is expensive to calculate, but never changes. However, there are uses for caches that expire after some time. I just can't think of any where I'd want them to be accessed via a property instead of a function call.
Having multiple decorators would seem to make this slightly more complicated.
I don't see why. The cache decorator could expose an interface to expire the cached value. Here's a quick&dirty but working solution. (Tested under Python 2.5.) def once(func): class Cache(object): def __call__(self, *args, **kwargs): try: return self._value except AttributeError: result = func(*args, **kwargs) self._value = result return result def expire(self): del self._value return Cache() class Test(object): @property @once def expensive(self): import time time.sleep(20) return 1 Works like a charm :) (At least for a solution I knocked up in 30 seconds.) The only downside is that to expire the cache, you need the not-very-obvious call: Test.expensive.fget.expire() rather than t.expensive.expire(), which can't work for obvious reasons.
I'm not exactly sure that a solution that doesn't work can be said to "easily deal with" anything :) -- Steven D'Aprano

Steven D'Aprano wrote:
This is slightly better (name change as in Antoine Pitrou's comment): class cached(object): def __init__(self, function): self._function = function self._cache = {} def __call__(self, *args): try: return self._cache[args] except KeyError: self._cache[args] = self._function(*args) return self._cache[args] def expire(self, *args): del self._cache[args]
Similarly, I can use: class Test(object): @property @cached def expensive(self): import time time.sleep(20) return 1 t = Test() and use: t.expensive
I'll bet you cannot quite do that; I need to do: Test.expensive.fget.expire(t) Note that 'cached' can do multi-arg functions, though it doesn't handle kwargs, since there is some ambiguity between calling keyword-provided args and the args vector. The change to cache redundant uses would be something like: - def __call__(self, *args): + def __call__(self, *args, **kwargs): + args = args, tuple(sorted(kwargs.items())): - def expire(self, *args): + def expire(self, *args, **kwargs)): + args = args, tuple(sorted(kwargs.items())): However, this might be fairly counter-intuitive (specifically when expiring some values, and thinking you have them all). --Scott David Daniels Scott.Daniels@Acm.Org

Le Thu, 30 Apr 2009 12:22:48 -0700, Scott David Daniels <Scott.Daniels@Acm.Org> s'exprima ainsi:
(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. Denis ------ la vita e estrany

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

George Sakkis <george.sakkis@gmail.com> writes:
Nevertheless, the “memoize” pattern *is* well-understood, and already implemented for Python as discussed earlier. It covers the “cached property” as a degenerate (i.e. simpler) case. Making a specific decorator that *only* addresses “cached property” is too narrow, IMO. I think a “memoize” decorator (by whatever name) is the right level of generality to address this use case, and has the advantage of existing implementations. -- \ “As I bit into the nectarine, it had a crisp juiciness about it | `\ that was very pleasurable - until I realized it wasn't a | _o__) nectarine at all, but A HUMAN HEAD!!” —Jack Handey | Ben Finney

On Thu, Apr 30, 2009 at 7:37 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
IMHO no existing implementation is good enough for addition to the standard library. Off the top of my head, some of the issues a general and robust implementation should address are: - Non hashable parameters. - Caching keys based on a subset of the provided parameters (or perhaps more general a key function). - Bounded-length caches. - Different expiration policies for bounded-length caches. - As good performance as possible given the previous constraints. Of course one can write a vanilla version in a few lines, and indeed searching for "memoize" in code.activestate.com returns no less than 125 results (including one I posted 4.5 years ago). I think the sheer number of the posted recipes is an indication that none of them has "solved" the problem for good, otherwise people would just reuse it instead of keep posting alternatives. George

On Thu, Apr 30, 2009, George Sakkis wrote:
Then a full-blown PEP is required. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "If you think it's expensive to hire a professional to do the job, wait until you hire an amateur." --Red Adair

On Fri, 1 May 2009 10:16:11 am George Sakkis wrote:
Or people just like re-inventing the wheel. By all means go ahead and write a PEP and develop an heavyweight "deal with everything including the kitchen sink" solution. But in the meantime, the standard library could do with a couple of nice, simple, lightweight memoize decorators. -- Steven D'Aprano

On Fri, 1 May 2009 09:27:13 am George Sakkis wrote:
In my opinion, properties aren't special enough to need a special cachedproperty() decorator. Especially not get-only properties that never expire, since they're just a function taking a single argument that returns a constant result. A generic cache that runs on any function should be sufficient, and more useful than one that is specific to properties. Properties are not the only application for caching expensive function calls that never change. -- Steven D'Aprano

On 30 Apr 2009, at 12:22, Scott David Daniels wrote:
The only thing I dislike is how many dictionary lookups are required in order to return the value after it's been cached. I count 4 lookups (object.prop, prop.__call__, self._cache, and self._cache[args]). These add up, especially if object.prop could have returned the value immediately without having to go through so much indirection (but this is not currently possible) Jared

On Fri, 1 May 2009 06:59:55 am Jared Grubb wrote:
But you shouldn't be comparing a cheap attribute requiring one key lookup to a cache requiring four lookups. You should be comparing an expensive function call to four lookups. If the function isn't expensive, there's no value in caching it. -- Steven D'Aprano

Steven D'Aprano <steve@...> writes:
-1 on an extra parameter to property.
+1 on a cache decorator.
+1 for calling it "cached" or "memoize". I don't know if it has its place in the builtin namespace, though. Putting it in functools would be fine. (it may need a way to flush/invalidate the cache, too: @cached def foo(l=[4]): l[0] += 1 return 4 print foo() # 5 print foo() # 5 foo.flush() print foo() # 6 )

I agree, -1 on modifying @property and +1 on adding a new decorator to functools or some other module, maybe a collection of useful decorators. Cheers, Daniel -- Psss, psss, put it down! - http://www.cafepress.com/putitdown

Le Thu, 30 Apr 2009 05:55:41 -0700, Daniel Fetchinson <fetchinson@googlemail.com> s'exprima ainsi:
While I regularly use caching/memoizing (e.g. for packrat parsing) and I like the idea of having it predefined in the language, now I wonder whether it's really worth it. Because usually it's a very simple thing to implement -- often two obvious lines of code -- and easy to understand, even for someone who does not know the principle yet. E.g for a property getter: def _getThing(...): if self._thing is not None: return self._thing <else compute thing> (Also note that for a func you can cache on the func itself: func.cache -- but the 2-3 cases when I thought at that, it was rather a design issue with global vars.) Maybe I'm missing the point? Denis ------ la vita e estrany

spir <denis.spir@...> writes:
now I wonder whether it's really worth it. Because usually it's a very simple thing to implement -- often two obvious lines of code -- and easy to
understand, even for someone who does
not know the principle yet.
Well, for one, a generic implementation may have to be thread-safe. Also, while it's easy to implement, it's the kind of useful primitive - like enumerate() and others - which is nice to have builtin in the language or the standard library. Regards Antoine.

Benjamin Peterson <benjamin@python.org> writes:
I would prefer this as a decorator (not affecting the function signature), and applicable to any function (not just a property). This is the “memoize” pattern, implemented as a decorator in <URL:http://code.activestate.com/recipes/496879/>. -- \ Lucifer: “Just sign the Contract, sir, and the Piano is yours.” | `\ Ray: “Sheesh! This is long! Mind if I sign it now and read it | _o__) later?” —http://www.achewood.com/ | Ben Finney
participants (15)
-
Aahz
-
Antoine Pitrou
-
Arnaud Delobelle
-
Ben Finney
-
Benjamin Peterson
-
Chris Rebert
-
Daniel Fetchinson
-
George Sakkis
-
Jared Grubb
-
Jeremy Banks
-
Mathias Panzenböck
-
Oleg Broytmann
-
Scott David Daniels
-
spir
-
Steven D'Aprano