我正在尝试制作一个计时器装饰器,它会对具有各种大小的列表的函数进行计时。这个计时器适用于insert(),我在下面写过,但不适用于递归函数,例如minimum()。对于任何大小超过1的列表,我都会收到递归深度错误。我应该如何解决这个问题?
def time_long_list(func):
import random
def helper(*args, **kwargs):
for item in [10, 100, 1000, 10000]:
list = [random.randint(1, 10) for element in range(item)]
with Timer() as clock:
func(list)
print(clock.interval/item)
return func
return helper
@time_long_list
def insert(lst):
new_list = []
for item in lst:
new_list.insert(0, item)
return new_list
@time_long_list
def minimum(lst):
if len(lst) == 0:
return None
elif len(lst) == 1:
return lst[0]
else:
mid = len(lst) // 2
min1 = minimum(lst[:mid])
min2 = minimum(lst[mid:])
if min1 <= min2:
return min1
else:
return min2
insert()
minimum()
答案 0 :(得分:1)
当你装饰一个函数时,你告诉python解释器接受函数,对它做“一些东西”,然后在global / module命名空间中存储对新的,修改过的函数的引用。
根据定义,递归是一个调用自身的函数。
所以你在这里尝试做的事情是行不通的,因为你的函数现在用所有代码装饰来生成测试数据。当您以递归方式调用函数时,每次都会生成所有测试数据。由于每一步都会生成新的测试数据,因此您永远无法到达递归的底部,并且不可避免地 - 您达到了深度限制。
我们需要的是在装饰函数时保持对原始函数的引用。然后,您将拥有两个函数,一个生成测试数据并执行顶级递归,另一个不生成测试数据。这不是很纯粹的递归 - 因为你有两个函数而不是一个,但它可能是你能做的最好的。
通过修改装饰器,我们可以保留对原始函数的引用
def time_long_list(func):
import random
def helper(*args, **kwargs):
for item in [10, 100, 1000, 10000]:
list = [random.randint(1, 10) for element in range(item)]
with Timer() as clock:
func(list, func)
print(clock.interval/item)
return func
helper.original = func
return helper
然后修改递归函数,使其始终调用自身的原始版本,而不是修改版本
@time_long_list
def minimum(lst, undecorated_func = None):
if len(lst) == 0:
return None
elif len(lst) == 1:
return lst[0]
else:
mid = len(lst) // 2
min1 = minimum.original(lst[:mid])
min2 = minimum.original(lst[mid:])
if min1 <= min2:
return min1
else:
return min2
我不建议这是一个很好的方法 - 你会更好 - 正如评论建议的那样,创建一个单独的顶级函数来进行计时和测试数据创建(如果可能的话)。