jianglai jianglai - 1 month ago 8
Python Question

Decorating recursive functions in python

I am having hard time understanding 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))


The output is:

(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 one prints f(n) so naturally it prints 'Original' every time f(n) is called recursively.

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

The third one puzzles me, which is the same as using decorator @dec. Why does decorated f(n) calls the wrapper five times also? It looks to me that def_f=dec(f) and f=dec(f) are just two keywords bound to two identical function objects. Is there something else going on when the decorated function is given the same name as the undecorated one?

Thanks!

Answer

As you said, the first one is called as usual.

the second one puts a decorated version of f called dec_f in the global scope. Dec_f is called, so that prints "Decorated!", but inside the f function passed to dec, you call f itself, not dec_f. the name f is looked up and found in the global scope, where it is still defined without the wrapper, so from than on, only f gets called.

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