为函数设置Python递归限制

12 投票
4 回答
5822 浏览
提问于 2025-04-16 22:54

我有两个解决方案,想用来处理一个递归问题,这个问题需要在一个函数(其实是一个方法)中实现。我希望这个函数是递归的,但我想把递归的深度限制在10次,并且在函数调用后重置这个限制(或者根本不去调整递归深度的限制)。有没有人能想到更好的方法,或者推荐我使用其中一个方案?我倾向于使用上下文管理器,因为这样可以让我的代码更整洁,而且不需要设置追踪限制,但可能会有一些注意事项?

import sys

def func(i=1):
    print i
    if i > 10:
        import sys
        sys.tracebacklimit = 1
        raise ValueError("Recursion Limit")
    i += 1
    func(i)

class recursion_limit(object):
    def __init__(self, val):
        self.val = val
        self.old_val = sys.getrecursionlimit()
    def __enter__(self):
        sys.setrecursionlimit(self.val)
    def __exit__(self, *args):
        sys.setrecursionlimit(self.old_val)
        raise ValueError("Recursion Limit")

def func2(i=1):
    """
    Call as

    with recursion_limit(12):
        func2()
    """
    print i
    i += 1
    func2(i)

if __name__ == "__main__":
    #    print 'Running func1'
    #    func()

    with recursion_limit(12):
        func2()

不过,我确实发现上下文管理器有些奇怪的行为。如果我在主程序中运行

with recursion_limit(12):
    func2()

它会打印1到10。如果我在解释器中运行同样的代码,它却打印1到11。我猜在我导入东西的时候,内部发生了一些事情?

补充说明:为了记录,这就是我为一个能知道自己调用深度的函数所想出的代码。我怀疑我不会在任何生产代码中使用它,但它确实能完成任务。

import sys
import inspect
class KeepTrack(object):
    def __init__(self):
        self.calldepth = sys.maxint

    def func(self):
        zero = len(inspect.stack())
        if zero < self.calldepth:
            self.calldepth = zero
        i = len(inspect.stack())
        print i - self.calldepth
        if i - self.calldepth < 9:
            self.func()

keeping_track = KeepTrack()
keeping_track.func()

4 个回答

1

忽略更一般的问题,看起来你可以通过查看inspect.getouterframes()的长度来获取当前的帧深度。这会给你一个“零点”,从这个点你可以设置深度限制(免责声明:我还没有尝试过这个)。

编辑:或者可以用len(inspect.stack()),我不太清楚这两者有什么区别。我很想知道这是否有效,以及它们是否不同。

1

虽然这个话题有点偏离主题(我本来想把它放在评论里,但我觉得空间不够),不过需要提一下的是,setrecursionlimit这个名字有点误导人——它实际上是设置了最大堆栈深度:

http://docs.python.org/library/sys.html#sys.setrecursionlimit

这就是为什么这个函数在不同地方调用时表现会不一样的原因。另外,如果func2调用了标准库中的某个函数(或者其他什么),导致堆栈深度增加超过了N,那么异常会提前触发。

还有,我也不建议你去改sys.tracebacklimit;这会影响到你程序的其他部分。还是听Ned的建议吧。

7

你不应该去改变系统的递归限制。你应该让你的函数自己知道它递归的深度,并在深度过大时停止递归。

你会发现你的程序和解释器中递归限制的表现不一样,是因为它们的调用栈顶层不同:解释器在运行你的代码之前,会调用一些函数。

撰写回答