Python – Cache function and decorator

cachingdecoratorpython

I am playing with cache functions using decorators.

First, I use a generic function

def memorize(func):
    cache = {}
    print "printing cache"
    print cache
    print "cache printed"    
    def decor_func(*args):
        if args in cache:
            print "cached value is found"
            return cache[args]
        else:
            print "calculating the value"
            val = func(*args)
            cache[args] = val
            return val
    return decor_func

Then I use it to decorate a function called test

def test(n):
    return n

test_decorated = memorize(test)

Then I get the following results

>>> test_decorated(1)
calculating the value
1
>>> test_decorated(2)
calculating the value
2
>>> test_decorated(1)
cached value is found
1
>>>

You can see that the second test_decorated(1) won't actually do any calculation, because we have already cache the result.

Now I want understand further, so I define

def test_cached(n):
    cache = {}
    print "printing cache"
    print cache
    print "cache printed"    
    if n in cache:
        print "cached value is found"
        return cache[n]
    else:
        print "calculating the value"
        val = n
        cache[n] = val
        return val

I thought that I will get the same results as above when I call test_cached.
However, the answer is NO, and the function test_cached didn't remember any calculated values. Here is the result:

>>> test_cached(1)
printing cache
{}
cache printed
calculating the value
1
>>> test_cached(2)
printing cache
{}
cache printed
calculating the value
2
>>> test_cached(1)
printing cache
{}
cache printed
calculating the value
1
>>> 

As you can see, the second test_cached(1) re-calculate again the result.

I would like to know why these two approaches give different results, i.e.
why the first approach successfully caches the results, but the second fails

Best Answer

cache is initialised when you do cache = {} which happens every time you call test_cached. Thus, the case is always empty (and useless) when you want to use it.

In your original code, cache is also initialised when you do cache = {} but you only do this when you call memorize which is done only once : when you do : test_decorated = memorize(test).

When you call test_decorated, it kind of remembers which cache object is to be used (and modified if needed).

(If you want to learn more about this, it might be interesting to look up for closures in Python).