views:

462

answers:

4

Consider the following:

@property
def name(self):

    if not hasattr(self, '_name'):

     # expensive calculation
     self._name = 1 + 1

    return self._name

I'm new, but I think the caching could be factored out into a decorator. Only I didn't find one like it ;)

PS the real calculation doesn't depend on mutable values

+5  A: 

There are memoizing decorators that perform what you call "caching", e.g. http://snippets.dzone.com/posts/show/4840 -- they typically work on functions as such (whether meant to become methods or not) whose results depend on their arguments (not on mutable things such as self!-) and so keep a separate memo-dict.

Alex Martelli
+4  A: 

It sounds like you're not asking for a general-purpose memoization decorator (i.e., you're not interested in the general case where you want to cache return values for different argument values). That is, you'd like to have this:

x = obj.name  # expensive
y = obj.name  # cheap

while a general-purpose memoization decorator would give you this:

x = obj.name()  # expensive
y = obj.name()  # cheap

I submit that the method-call syntax is better style, because it suggests the possibility of expensive computation while the property syntax suggests a quick lookup.

[Update: The class-based memoization decorator I had linked to and quoted here previously doesn't work for methods. I've replaced it with a decorator function.] If you're willing to use a general-purpose memoization decorator, here's a simple one:

def memoize(function):
  memo = {}
  def wrapper(*args):
    if args in memo:
      return memo[args]
    else:
      rv = function(*args)
      memo[args] = rv
      return rv
  return wrapper

Example usage:

@memoize
def fibonacci(n):
  if n < 2: return n
  return fibonacci(n - 1) + fibonacci(n - 2)

Another memoization decorator with a limit on the cache size can be found here.

Nathan Kitchen
None of the decorators mentioned in all the answers work for methods!Probably because they're class-based. Only one self is passed?Others work fine, but it's crufty to store values in functions.
Tobias
I think you may run into a problem if args is not hashable.
Unknown
@Unknown Yes, the first decorator that I quoted here is limited to hashable types. The one at ActiveState (with the cache size limit) pickles the arguments into a (hashable) string which is of course more expensive but more general.
Nathan Kitchen
@vanity Thanks for pointing out the limitations of the class-based decorators. I've revised my answer to show a decorator function, which works for methods (I actually tested this one).
Nathan Kitchen
+1  A: 

Ah, just needed to find the right name for this: "Lazy property evaluation".

I do this a lot too; maybe I'll use that recipe in my code sometime.

Ken Arnold
+1  A: 

There is yet another example of a memoize decorator at Python Wiki:

http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize

That example is a bit smart, because it won't cache the results if the parameters are mutable. (check that code, it's very simple and interesting!)

Denilson Sá