[Python-Dev] PEP 318: Let's propose some useful built-in decorators

Michael Chermside mcherm at mcherm.com
Mon Apr 5 10:03:46 EDT 2004

Guido writes:
> While I ponder the decorator syntax, let's propose some built-in
> decorators.
> Proposals for other standard decorators are welcome!

memoized -- Encloses the function in a wrapper which provides automatic
   memoization. Provides two different uses. The simple use is just this:

       def fibonacci(n) [memoized()]:
           if n == 0: return 0
           elif n == 1: return 1
           else return fibonacci(n-1) + fibonacci(n-2)

   ...which automatically memoizes all results. For advanced users who
   want to limit memory use, there's this:

       def fibonacci(n) [memoized(cache_size=25)]:

   ... which will save only the 25 most-recently-used values.

Here... I'll try to implement it, since I want to play around with
writing a decorator which takes arguments. I'll see if I can do so in
a highly readable manner:

    # WARNING... Untested code follows!

    def memoized(cache_size=None):
        if cache_size is None:
            # No cache size limits, so just store the values in a hash
            def memoize_decorator(f):
                memo_cache = {}
                def wrapper_function(*args, **kwargs):
                    if memo_cache.has_key((args, kwargs)):
                        return memo_cache[(args, kwargs)]
                        value = f(*args, **kwargs)
                        memo_cache[(args, kwargs)] = value
                        elif not memo_cache_full:
                        return value
                return wrapper_function

            # cache_size is given, so we need a LRU cache.
            #   This currently uses a dirt-simple LRU cache... a list
            #   of (key, value) pairs with new items added to the end.
            #   Performance could be improved with a fancier data
            #   structure.
            def memoize_decorator(f):
                memo_cache = []
                def wrapper_function(*args, **kwargs):
                    for key, value in memo_cache:
                        if key == (args, kwargs):
                            # Found it in the cache! Move to end of list.
                            memo_cache.remove( ((args, kwargs), value) )
                            memo_cache.append( ((args, kwargs), value) )
                    # Not found in cache... must calculate
                    value = f(*args, **kwargs)
                    memo_cache.append( ((args, kwargs), value) )
                    if len(memo_cache) > cache_size:
                    return value
                return wrapper_function

        return memoize_decorator

Hmm.... somewhat awkward, but readable. Nested function definitions and
nested scoping rules make this much easier than otherwise. Nevertheless,
a class-based approach might have worked better.

-- Michael Chermside

More information about the Python-Dev mailing list