难以理解递归修饰函数

时间:2017-08-10 18:46:45

标签: python

我有以下代码,我正在装饰递归函数,但我无法理解代码的某些部分。

def deco_func(f):
    def wrapper(*args):
        print('Decorating', args)
        res = f(*args)
        print(res)

    return wrapper


@deco_func
def fact(n):
    if n == 0:
        return 1
    else:
        return n * fact(n - 1)

fact(4)

注意:

1。包装函数中的f实际指向事实函数的原始版本,因为它来自封闭范围,其值由包装器关闭。

2。事实函数内的递归调用调用了包装的事实的装饰版本。

根据我的理解,堆栈跟踪看起来像这样。

res = f(4)
    = 4 * wrapper(3) = 4 * f(3)
    = 4 * 3 * wrapper(2) = 4 * 3 * f(2)
    = 4 * 3 * 2 * wrapper(1) = 4 * 3 * 2 * f(1)
    = 4 * 3 * 2 * 1 * wrapper(0) = 4 * 3 * 2 * 1 * f(0)

此时n将为0,因此f(0)将返回1,因此堆栈将如下所示

res = 4 * 3 * 2 * 1 * 1

所以res应为24,但其值为1,我无法理解。此时我也得到了一个例外

TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'

但是只要我在包装函数的底部添加一个返回语句,如return res,一切正常。

我不明白为什么没有返回就是在这里创建一个问题,因为在包装函数控件内部永远不会超出res = f(*args)直到递归完成。因此,像f(0)之类的最终调用将导致res = 5*4*3*2*1,并且如上所示,res应该被评估为24,然后控件应该转到下一个语句。那么为什么不在包装器中使用return语句会在返回仅在计算res后执行时产生问题

我知道我在这里缺少一些重要的东西,但是如果我们删除return语句,我无法从概念上弄清楚出了什么问题。

2 个答案:

答案 0 :(得分:1)

装饰器替换该函数,因此当您在fact函数中引用fact时,您实际上是指装饰函数,即{ {1}}。因此,对于每个递归级别调用wrapper一次,并且当展开返回值时,wrapper返回的值是wrapper,当您没有显式返回语句时。None

要正常工作,wrapper必须返回res值,以便递归正确展开。

def deco_func(f):
    def wrapper(*args):
        print('Decorating', args)
        res = f(*args) # calls undecorated 'fact'
        print(res)
        return res # !!! so None is not returned implicitly

    return wrapper


@deco_func
def fact(n):
    if n == 0:
        return 1
    else:
        return n * fact(n - 1) # calls 'wrapper' (aka decorated 'fact')

fact(4)

答案 1 :(得分:0)

您的原始函数f会返回一个数值。

您的装饰功能没有,这意味着它返回None。这就是递归调用尝试将n乘以None的原因,因此错误。

缺失的部分是return res