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

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)]
                else:
                    value = f(*args, **kwargs)
                    memo_cache[(args, kwargs)] = value
                    elif not memo_cache_full:
                        
                    return value
            return wrapper_function

    else:
        # 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:
                    memo_cache.pop(0)
                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