Understanding parameter handling in the python reminder decorator

I used this excellent decorator for memoization, which I found on the Internet (here is an example of a Fibonacci sequence):

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 would like to understand the inner workings a little better, I did not find answers after reading about decorators or parameter handling in Python.

Why isn't the cache dictionary reinitialized every time a decorated function is called?

How are * x parameters sent to the decorated function recognized, i.e. 969 in the function call fib (969)?

+7
source share
1 answer

The decorator is called only once, right after a certain function is first defined. So these two methods (using @wrap and bar = wrap (bar)) are the same:

 >>> def wrap(f): ... print 'making arr' ... arr = [] ... def inner(): ... arr.append(2) ... print arr ... f() ... return inner ... >>> @wrap ... def foo(): ... print 'foo was called' ... making arr >>> foo() [2] foo was called >>> foo() [2, 2] foo was called >>> def bar(): ... print 'bar was called' ... >>> bar = wrap(bar) making arr >>> bar() [2] bar was called 

In both cases, it is clear that arr is only created when wrap (f) is called, and wrap is only called when foo and bar are declared first.

Regarding the case of passing arguments to a decorated function, remember that the decorator takes the function as a parameter and returns a modified version of this function. Therefore, the decorator usually takes one parameter, which is the function that it modifies. It returns a new function, and the decorator can define the function that it returns by accepting any number of arguments (e.g. * args). The decorator may even return a function that takes too many parameters for the method that it decorates.

 >>> def wrap_with_arg(f): ... def wrap(*args): ... print 'called with %d arguments' % len(args) ... f(args) ... return wrap ... >>> @wrap_with_arg ... def baz(arg): ... print 'called with argument %r' % arg ... >>> baz(3) called with 1 arguments called with argument 3 >>> baz(3, 4) called with 2 arguments Traceback (most recent call last): File "<input>", line 1, in <module> File "<input>", line 4, in wrap File "<input>", line 3, in baz TypeError: not all arguments converted during string formatting 

While baz throws an error, note how the number of arguments is correctly printed before the error is reset.

+9
source

All Articles