Digamos que tengo una función Python f
y fhelp
. fhelp
está diseñado para llamarse a sí mismo de forma recursiva. f
no debe ser llamado recursivamente. ¿Hay alguna manera para que f
determine si se ha llamado recursivamente?
Utilice el módulo de rastreo para esto:
>>> import traceback >>> def f(depth=0): ... print depth, traceback.print_stack() ... if depth < 2: ... f(depth + 1) ... >>> f() 0 File "", line 1, in File "", line 2, in f None 1 File " ", line 1, in File "", line 4, in f File " ", line 2, in f None 2 File " ", line 1, in File "", line 4, in f File " ", line 4, in f File " ", line 2, in f None
Entonces, si alguna entrada en la stack indica que el código fue llamado desde f
, la llamada fue (en) directamente recursiva. El método traceback.extract_stack
le brinda un acceso fácil a estos datos. La statement if len(l[2] ...
en el ejemplo a continuación simplemente cuenta el número de coincidencias exactas del nombre de la función. Para hacerlo aún más bonito (gracias a agf por la idea), podría convertirlo en un decorador:
>>> 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 "", line 1, in File "", line 5, in func File " ", line 4, in foo File " ", line 5, in func Exception: Recursed
Podrías usar una bandera puesta por un decorador:
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
Entonces puedes hacer
@norecurse def f(some, arg, s): do_stuff()
y si se vuelve a llamar a f
mientras se está ejecutando, se called
True
y generará una excepción.