装饰器可以装饰递归函数吗?

时间:2020-02-05 00:35:58

标签: python python-3.x recursion decorator

我想看看两种计算斐波那契数列的方法之间的时间成本差异: 首先,我创建了一个装饰器,将“输出时间成本”函数添加到一个函数中:

def time_cost(func):
    def wed(n):
        start = time.time()
        func(n)
        stop = time.time()
        print(stop-start)
    return wed

然后我写了第一个函数:

@time_cost
def DP_F(n):
    f = [1,1]
    while len(f)<n:
    f.append(f[len(f)-1]+f[len(f)-2])
    return f

效果很好

>>> DP_F(10)
0.0
>>> DP_F(100)
0.0
>>> DP_F(10000)
0.007944107055664062

但是当我用装饰器创建第二个函数时发生了错误:

@time_cost
def R_F(n):
    if n<=2:
        return 1
    else:
        return R_F(n-1)+R_F(n-2)

引发错误,提示某些输出可能会丢失

>>> R_F(10)
0.0
0.0
Traceback (most recent call last):
  File "<pyshell#44>", line 1, in <module>
    R_F(10)
  File "<pyshell#28>", line 4, in wed
    func(n)
  File "<pyshell#43>", line 8, in R_F
    return R_F(n-1)+R_F(n-2)
  File "<pyshell#28>", line 4, in wed
    func(n)
  File "<pyshell#43>", line 8, in R_F
    return R_F(n-1)+R_F(n-2)
  File "<pyshell#28>", line 4, in wed
    func(n)
  File "<pyshell#43>", line 8, in R_F
    return R_F(n-1)+R_F(n-2)
  File "<pyshell#28>", line 4, in wed
    func(n)
  File "<pyshell#43>", line 8, in R_F
    return R_F(n-1)+R_F(n-2)
  File "<pyshell#28>", line 4, in wed
    func(n)
  File "<pyshell#43>", line 8, in R_F
    return R_F(n-1)+R_F(n-2)
  File "<pyshell#28>", line 4, in wed
    func(n)
  File "<pyshell#43>", line 8, in R_F
    return R_F(n-1)+R_F(n-2)
  File "<pyshell#28>", line 4, in wed
    func(n)
  File "<pyshell#43>", line 8, in R_F
    return R_F(n-1)+R_F(n-2)
  File "<pyshell#28>", line 4, in wed
    func(n)
  File "<pyshell#43>", line 8, in R_F
    return R_F(n-1)+R_F(n-2)
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'

因此Python装饰器无法装饰递归函数吗?

1 个答案:

答案 0 :(得分:2)

当前的问题是wed不返回func的返回值。很容易解决。

def time_cost(func):
    def wed(n):
        start = time.time()
        n = func(n)
        stop = time.time()
        print(stop-start)
        return n
    return wed

但是,现在看一下致电R_F(3)时会发生什么。

>>> R_F(3)
9.5367431640625e-07
1.1920928955078125e-06
0.0001671314239501953
2

您获得 3 次:每个递归调用一次。这是因为原始函数调用了绑定到的任何R_F,现在是函数wed,而不是实际的斐波那契函数。

使用上下文管理器可以更好地处理这种情况。

from contextlib import contextmanager

@contextmanager
def time_cost():
    start = time.time()
    yield
    stop = time.time()
    print(stop - start)

with time_cost():
    R_F(3)

离题

从某种意义上说,Python没有递归函数。一个函数不能调用自身,而只有绑定到您期望的名称的某个函数会引用您的函数。称之为“合作递归”。

例如,考虑递归函数的标准示例,阶乘。

def fact(x):
     return 1 if x == 0 else x * fact(x-1)

我们可以通过重新绑定名称fact来轻松解决这个问题。

g = fact  # save a reference to the original function
def fact(x):
   print("Broken")
   return 0

现在g(3)打印Broken并返回0,因为它将尝试调用绑定到 now 的任何fact,而不是调用fact在您重新定义它之前就已经绑定了。

如果要使用“安全”递归函数,则必须使用私有递归帮助器对其进行定义。

def fact(x):
    def helper(x):
        return 1 if x == 0 else x * helper(x - 1)
    return helper(x)

现在您可以安全地装饰fact,因为无论绑定到fact(无论是原始功能还是装饰功能),helper都不会反弹。