I have been using this excellent decorator for memoization, which I found on the web (shown here with the Fibonacci sequence as an example):
def memoize(f):
cache= {}
def memf(*x):
if x not in cache:
cache[x] = f(*x)
return cache[x]
return memf
@memoize
def fib(n):
if n==1 or n==0:
return 1
return fib(n-2) + fib(n-1)
print fib(969)
Now I’d like to understand the inner workings a bit better, I have not found answers by reading up on decorators or parameter handling in Python.
Why is the cache dictionary not reinitialized every time the decorated function is called?
How is *x recognized to be the parameters sent to the decorated function, i.e., 969 in the function call fib(969)?
The decorator is called only once, immediately after the decorated function is first defined. Thus, these two techniques(using @wrap and bar = wrap(bar)) are the same:
In both cases it is clear that arr is created only when wrap(f) is called, and wrap is called only when foo and bar are first declared.
As for the case of passing arguments to a decorated function, remember that a decorator takes a function as a parameter and returns a modified version of that function. So a decorator typically takes one parameter, which is the function that it is modifying. It returns a new function, and the decorator can define the function that it returns as taking any number of arguments(for example, *args). The decorator can even return a function that takes too many parameters for the method that it decorates.
While eventually baz throws an error, notice how the number of arguments is correctly printed before the error is thrown.