为函数设置Python递归限制
我有两个解决方案,想用来处理一个递归问题,这个问题需要在一个函数(其实是一个方法)中实现。我希望这个函数是递归的,但我想把递归的深度限制在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 个回答
忽略更一般的问题,看起来你可以通过查看inspect.getouterframes()的长度来获取当前的帧深度。这会给你一个“零点”,从这个点你可以设置深度限制(免责声明:我还没有尝试过这个)。
编辑:或者可以用len(inspect.stack()),我不太清楚这两者有什么区别。我很想知道这是否有效,以及它们是否不同。
虽然这个话题有点偏离主题(我本来想把它放在评论里,但我觉得空间不够),不过需要提一下的是,setrecursionlimit这个名字有点误导人——它实际上是设置了最大堆栈深度:
http://docs.python.org/library/sys.html#sys.setrecursionlimit
这就是为什么这个函数在不同地方调用时表现会不一样的原因。另外,如果func2调用了标准库中的某个函数(或者其他什么),导致堆栈深度增加超过了N,那么异常会提前触发。
还有,我也不建议你去改sys.tracebacklimit;这会影响到你程序的其他部分。还是听Ned的建议吧。
你不应该去改变系统的递归限制。你应该让你的函数自己知道它递归的深度,并在深度过大时停止递归。
你会发现你的程序和解释器中递归限制的表现不一样,是因为它们的调用栈顶层不同:解释器在运行你的代码之前,会调用一些函数。