Decorating recursive functions in python

I find it hard to understand how a decorated recursive function works. For the following snippet:

def dec(f): def wrapper(*argv): print(argv, 'Decorated!') return(f(*argv)) return(wrapper) def f(n): print(n, 'Original!') if n == 1: return(1) else: return(f(n - 1) + n) print(f(5)) print dec_f = dec(f) print(dec_f(5)) print f = dec(f) print(f(5)) 

Output:

 (5, 'Original!') (4, 'Original!') (3, 'Original!') (2, 'Original!') (1, 'Original!') 15 ((5,), 'Decorated!') (5, 'Original!') (4, 'Original!') (3, 'Original!') (2, 'Original!') (1, 'Original!') 15 ((5,), 'Decorated!') (5, 'Original!') ((4,), 'Decorated!') (4, 'Original!') ((3,), 'Decorated!') (3, 'Original!') ((2,), 'Decorated!') (2, 'Original!') ((1,), 'Decorated!') (1, 'Original!') 15 

The first prints f (n), so naturally, it prints "Original" every time f (n) is called recursively.

The second prints def_f (n), so when n is passed to the shell, it calls f (n) recursively. But the shell itself is not recursive, so only one "Decorated" is printed.

The third puzzles me, which is similar to using decorator @dec. Why is decorated f (n) also invoking a wrapper five times? It seems to me that def_f = dec (f) and f = dec (f) are just two keywords associated with two identical function objects. Is there anything else when the decorated function is given the same name as the untethered?

Thanks!

+7
source share
5 answers

As you said, the first is called as usual.

the second puts the decorated version of f dec_f in the global area. Dec_f is called so that it prints β€œDecorated!”, But inside the function f passed to dec, you call f itself, not dec_f. the name f is looked up and found in the global area where it is still defined without a shell, so only f is called because of it.

in example 3re, you assign a decorated version of the name f, so when inside the function f, the name f is looked up, it is looked in the global area, finds f, which is now a decorated version.

+4
source

The whole purpose of Python is just binding names to objects. When you

 f = dec(f) 

what you do is bind the name f to the return value of dec(f) . At this point, f no longer refers to the original function. The original function still exists and is called by the new f , but you no longer have a named reference to the original function.

+5
source

Your function calls something called f , which python scans in the scope.

As long as the operator f = dec(f) , f is still tied to the expanded function, so that's what is being called.

+1
source

If decorators indicate a prolog / epilogue to be done before or after another function, we can avoid this several times by simulating decorators with recursive functions.

For example:

 def timing(f): def wrapper(*args): t1 = time.clock(); r = apply(f,args) t2 = time.clock(); print"%f seconds" % (t2-t1) return r return wrapper @timing def fibonacci(n): if n==1 or n==2: return 1 return fibonacci(n-1)+fibonacci(n-2) r = fibonacci(5) print "Fibonacci of %d is %d" % (5,r) 

It produces:

 0.000000 seconds 0.000001 seconds 0.000026 seconds 0.000001 seconds 0.000030 seconds 0.000000 seconds 0.000001 seconds 0.000007 seconds 0.000045 seconds Fibonacci of 5 is 5 

We can simulate a decorator to force only one prolog / epilogue:

 r = timing(fibonacci)(5) print "Fibonacci %d of is %d" % (5,r) 

What produces:

 0.000010 seconds Fibonacci 5 of is 5 
0
source

Changed your code a bit

 def dec(func): def wrapper(*argv): print(argv, 'Decorated!') return(func(*argv)) return(wrapper) def f(n): print(n, 'Original!') if n == 1: return(1) else: return(f(n - 1) + n) print(f(5)) print dec_f = dec(f) print(dec_f(5)) print f = dec(f) print(f(5)) 

I think this will make things more understandable here, the wrapper function actually closes the func object from the scope. Therefore, every call to the func function inside the wrapper will call the original f, but a recursive call inside f will call the decorated version of f.

In fact, you can see this by simply printing out the func.__name__ inner wrapper and f.__name__ inside the f function

0
source

All Articles