在python中装饰递归函数

jia*_*lai 10 python recursion decorator

我很难理解装饰递归函数的工作原理.对于以下代码段:

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))
Run Code Online (Sandbox Code Playgroud)

输出是:

(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
Run Code Online (Sandbox Code Playgroud)

第一个打印f(n),因此每当f(n)被递归调用时,它自然会打印'Original'.

第二个打印def_f(n),所以当n传递给包装器时,它会递归调用f(n).但是包装器本身不是递归的,因此只打印一个"装饰".

第三个让我困惑,这与使用装饰器@dec相同.为什么装饰f(n)也会调用包装器五次?在我看来,def_f = dec(f)和f = dec(f)只是绑定到两个相同函数对象的两个关键字.当装饰函数与未修饰函数同名时,还有其他事情发生吗?

谢谢!

Joh*_*n Y 5

Python中的所有赋值都只是将名称绑定到对象.当你有

f = dec(f)
Run Code Online (Sandbox Code Playgroud)

你正在做的是将名称绑定f到返回值dec(f).此时,f不再指原始功能.原始函数仍然存在并由new调用f,但您不再具有对原始函数的命名引用.


big*_*ind 5

正如你所说,第一个像往常一样被召唤.

第二个在全局范围内放置一个名为dec_f的f的装饰版本.调用Dec_f,因此打印"Decorative!",但在传递给dec的f函数内部,你调用f本身,而不是dec_f.查找名称f并在全局范围内找到它,它仍然在没有包装器的情况下定义,所以从而只有f被调用.

在3re示例中,您将装饰版本分配给名称f,因此当在函数f内部时,查找名称f,它在全局范围内查找,找到f,现在是装饰版本.