Python 使用decorator对递归函数计时

Python 使用decorator对递归函数计时,python,recursion,python-decorators,Python,Recursion,Python Decorators,我正在尝试制作一个计时器装饰器,用于对具有不同大小列表的函数进行计时。计时器适用于insert(),我在下面写过,但不适用于诸如minimum()之类的递归函数。对于任何大小大于1的列表,我都会得到一个递归深度错误。我该如何补救 def time_long_list(func): import random def helper(*args, **kwargs): for item in [10, 100, 1000, 10000]: li

我正在尝试制作一个计时器装饰器,用于对具有不同大小列表的函数进行计时。计时器适用于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()
def time_long_列表(func):
随机输入
def辅助程序(*args,**kwargs):
对于[10100100010000]中的项目:
列表=[范围(项)中元素的random.randint(1,10)]
使用计时器()作为时钟:
func(列表)
打印(时钟间隔/项目)
返回函数
返回助手
@时间长列表
def插件(lst):
新列表=[]
对于lst中的项目:
新列表。插入(0,项)
返回新列表
@时间长列表
def最小值(lst):
如果len(lst)==0:
一无所获
elif len(lst)=1:
返回lst[0]
其他:
mid=len(lst)//2
min1=最小值(lst[:mid])
min2=最小值(lst[mid:])

如果min1在修饰函数时,告诉python解释器获取函数,对其执行“一些操作”,然后在全局/模块名称空间中存储对新修改函数的引用

根据定义,递归是一个调用自身的函数

因此,您在这里尝试执行的操作无法正常工作,因为您的函数现在已经用所有生成测试数据的代码进行了修饰。当您递归调用函数时,每次都会生成所有测试数据。由于在每一步都会生成新的测试数据,因此您永远无法到达递归的底部,并且不可避免地会达到深度限制

当我们装饰函数时,您需要保留对原始函数的引用。然后您将有两个函数,一个生成测试数据并执行顶级递归,另一个不生成测试数据。这不是纯粹的递归——因为你有两个函数,而不是一个,但这可能是你能做的最好的了

通过修改decorator,我们可以保留对原始函数的引用

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
@time\u long\u列表
def最小值(lst,未装饰功能=无):
如果len(lst)==0:
一无所获
elif len(lst)=1:
返回lst[0]
其他:
mid=len(lst)//2
min1=最小原始值(lst[:mid])
min2=最小原始值(lst[mid:]

如果min1的话,把它变成一个装饰师没有多大意义。您失去了对原始函数的访问权,当原始函数需要访问自身时,这尤其糟糕。因此,我是否应该定义一个新的函数变量mini=time\u long\u list(minimum)?这样就行了。或者,
time\u long\u list
可以直接为传递的函数计时,而不是生成计时包装器。装饰器没有错。但是如果你在递归函数上使用它,你可能想要修饰包装函数(也就是说,除非你对子计时感兴趣)。我见过递归函数的记忆修饰器,比如斐波那契函数。除非调用的数字约为1000,否则这些不会引发任何递归深度错误,而我的计时器装饰器在装饰最小值函数时失败,因为列表大小>=2。这两种情况有什么区别?