假设我有一个Python函数f
和fhelp
。 fhelp
旨在递归调用自身。 f
不应该递归调用。有没有办法让f
确定它是否已被递归调用?
答案 0 :(得分:13)
使用traceback模块:
>>> import traceback
>>> def f(depth=0):
... print depth, traceback.print_stack()
... if depth < 2:
... f(depth + 1)
...
>>> f()
0 File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
None
1 File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in f
File "<stdin>", line 2, in f
None
2 File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in f
File "<stdin>", line 4, in f
File "<stdin>", line 2, in f
None
因此,如果堆栈中的任何条目指示代码是从f
调用的,则调用是直接递归的。 traceback.extract_stack
方法可让您轻松访问此数据。以下示例中的if len(l[2] ...
语句只计算函数名称的完全匹配数。为了使它更漂亮(感谢agf的想法),你可以把它变成装饰者:
>>> def norecurse(f):
... def func(*args, **kwargs):
... if len([l[2] for l in traceback.extract_stack() if l[2] == f.func_name]) > 0:
... raise Exception, 'Recursed'
... return f(*args, **kwargs)
... return func
...
>>> @norecurse
... def foo(depth=0):
... print depth
... foo(depth + 1)
...
>>> foo()
0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in func
File "<stdin>", line 4, in foo
File "<stdin>", line 5, in func
Exception: Recursed
答案 1 :(得分:1)
您可以使用装饰器设置的标志:
def norecurse(func):
func.called = False
def f(*args, **kwargs):
if func.called:
print "Recursion!"
# func.called = False # if you are going to continue execution
raise Exception
func.called = True
result = func(*args, **kwargs)
func.called = False
return result
return f
然后你可以做
@norecurse
def f(some, arg, s):
do_stuff()
如果在f
运行时再次调用called
,则True
将为{{1}}并且会引发一个例外。