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 に答える