我有以下代码,我正在装饰递归函数,但我无法理解代码的某些部分。
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语句,我无法从概念上弄清楚出了什么问题。
答案 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
。