Python方法可以检查它是否已从内部调用?

时间:2011-10-26 08:31:49

标签: python recursion introspection inspect

假设我有一个Python函数ffhelpfhelp旨在递归调用自身。 f不应该递归调用。有没有办法让f确定它是否已被递归调用?

2 个答案:

答案 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}}并且会引发一个例外。