Python decorator可以修饰递归函数吗?
我想看看计算斐波那契序列的两种方法之间的时间成本差异: 首先,我创建了一个decorator,将“输出时间成本”函数添加到函数中:Python decorator可以修饰递归函数吗?,python,python-3.x,recursion,decorator,Python,Python 3.x,Recursion,Decorator,我想看看计算斐波那契序列的两种方法之间的时间成本差异: 首先,我创建了一个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
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
然后我编写了第一个函数:
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
但是当我用decorator创建第二个函数时,发生了一些错误:
@time_cost
def R_F(n):
if n<=2:
return 1
else:
return R_F(n-1)+R_F(n-2)
@时间成本
def R_F(n):
如果n>>R\u F(10)
0
0
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
R_F(10)
文件“”,第4行,星期三
func(n)
文件“”,第8行,在R\u F中
返回R_F(n-1)+R_F(n-2)
文件“”,第4行,星期三
func(n)
文件“”,第8行,在R\u F中
返回R_F(n-1)+R_F(n-2)
文件“”,第4行,星期三
func(n)
文件“”,第8行,在R\u F中
返回R_F(n-1)+R_F(n-2)
文件“”,第4行,星期三
func(n)
文件“”,第8行,在R\u F中
返回R_F(n-1)+R_F(n-2)
文件“”,第4行,星期三
func(n)
文件“”,第8行,在R\u F中
返回R_F(n-1)+R_F(n-2)
文件“”,第4行,星期三
func(n)
文件“”,第8行,在R\u F中
返回R_F(n-1)+R_F(n-2)
文件“”,第4行,星期三
func(n)
文件“”,第8行,在R\u F中
返回R_F(n-1)+R_F(n-2)
文件“”,第4行,星期三
func(n)
文件“”,第8行,在R\u F中
返回R_F(n-1)+R_F(n-2)
TypeError:+:“非类型”和“非类型”的操作数类型不受支持
所以Python装饰器不能装饰递归函数 直接的问题是
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
您将获得3次:每次递归调用一次。这是因为原始函数调用绑定到的任何R\u 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)
打印breaked
并返回0,因为它将尝试调用现在绑定到的fact
,而不是重新定义它之前绑定到的fact
如果你想要一个“安全”的递归函数,你必须用一个私有的递归助手来定义它
def fact(x):
def helper(x):
return 1 if x == 0 else x * helper(x - 1)
return helper(x)
现在您可以安全地修饰
fact
,因为无论fact
绑定到什么(无论是原始函数还是修饰函数),helper
都不会反弹。您的包装器在wed
内部返回None
,您调用了func(n)
(在本例中,可以是DP\u F
和R\u F
中的任意一个)不捕获并返回该结果。默认返回值变为None
,这解释了错误。这不是因为Python装饰程序无法装饰递归函数。可以,但重复调用的是装饰函数,而不是原始函数。每个“递归”调用将调用wed
,它启动一个新的计时器并执行一个新的print
语句。使用上下文管理器比使用装饰器更好地处理这种情况。
def fact(x):
def helper(x):
return 1 if x == 0 else x * helper(x - 1)
return helper(x)