The following function is meant to be used as a decorator that stores the results of already computed values. If the argument has already been calculated before, the function will return the value stored in the cache dictionary:
def cached(f):
f.cache = {}
def _cachedf(*args):
if args not in f.cache:
f.cache[args] = f(*args)
return f.cache[args]
return _cachedf
I realized (by mistake) that cache does not need to be an attribute of the function object. As a matter of facts, the following code works as well:
def cached(f):
cache = {} # <---- not an attribute this time!
def _cachedf(*args):
if args not in cache:
cache[args] = f(*args)
return cache[args]
return _cachedf
I am having a hard time understanding how can the cache object be persistent across multiple calls. I tried calling multiple cached functions several times and could not find any conflict or problems.
Can anyone please help me understand how the cache variable still exists even after the _cachedf function is returned?
You are creating a closure here: The function
_cachedf()closes over the variablecachefrom the enclosing scope. This keepscachealive as long as the function object lives.Edit: Maybe I should add a few more details on how this works in Python and how CPython implements this.
Let’s look at a simpler example:
Example usage in the interactive interpreter
During compilation of the module containing the function
f(), thecompiler sees that the function
g()references the nameafrom theenclosing scope and memorises this external reference in the code
object corresponding to the function
f()(specifically, it adds thename
atof.__code__.co_cellvars).So what happens when the function
f()is called? The first linecreate a new list object and binds it to the name
a. The next linecreates a new function object (using a the code object created during
the compilation of the module) and binds it to the name
g. The bodyof
g()isn’t executed at this point, and finally the funciton objectis returned.
Since the code object of
f()has a note that the nameaisreferenced by local functions, a “cell” for this name is created when
f()is entered. This cell contains the reference to the actual listobject
ais bound to, and the functiong()gets a reference tothis cell. That way, the list object and the cell are kept alive even
when the funciton
f()exits.