10

假设我有一个 Python 函数ffhelp. fhelp被设计为递归调用自身。f不应该递归调用。有没有办法f确定它是否被递归调用?

4

2 回答 2

16

为此使用回溯模块:

>>> 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
于 2011-10-26T08:35:26.443 回答
3

您可以使用装饰器设置的标志:

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并且它会引发异常。

于 2011-10-26T08:45:12.797 回答