2011-08-04 15 views
11

Tengo 2 soluciones para un problema de recursión que necesito para una función (en realidad un método). Quiero que sea recursivo, pero quiero establecer el límite de recursión en 10 y restablecerlo después de que se llame a la función (o no interferir con el límite de recursividad). ¿Alguien puede pensar en una mejor manera de hacer esto o recomendar usar uno sobre los demás? Me estoy inclinando hacia el administrador de contexto porque mantiene mi código más limpio y no establece el límite de seguimiento, pero puede haber advertencias?establecer el límite de recursión de python para una función

import sys 

def func(i=1): 
    print i 
    if i > 10: 
     import sys 
     sys.tracebacklimit = 1 
     raise ValueError("Recursion Limit") 
    i += 1 
    func(i) 

class recursion_limit(object): 
    def __init__(self, val): 
     self.val = val 
     self.old_val = sys.getrecursionlimit() 
    def __enter__(self): 
     sys.setrecursionlimit(self.val) 
    def __exit__(self, *args): 
     sys.setrecursionlimit(self.old_val) 
     raise ValueError("Recursion Limit") 

def func2(i=1): 
    """ 
    Call as 

    with recursion_limit(12): 
     func2() 
    """ 
    print i 
    i += 1 
    func2(i) 

if __name__ == "__main__": 
    # print 'Running func1' 
    # func() 

    with recursion_limit(12): 
     func2() 

Aunque veo un comportamiento extraño con el administrador de contexto. Si pongo en el principal

with recursion_limit(12): 
    func2() 

Imprime 1 a 10. Si hago lo mismo desde el intérprete imprime 1 a 11. Asumo que hay algo que hacer bajo el capó cuando la importación cosas?

EDITAR: Para la posteridad esto es lo que he encontrado para una función que conoce su profundidad de llamada. Dudo que lo use en ningún código de producción, pero hace el trabajo bien.

import sys 
import inspect 
class KeepTrack(object): 
    def __init__(self): 
     self.calldepth = sys.maxint 

    def func(self): 
     zero = len(inspect.stack()) 
     if zero < self.calldepth: 
      self.calldepth = zero 
     i = len(inspect.stack()) 
     print i - self.calldepth 
     if i - self.calldepth < 9: 
      self.func() 

keeping_track = KeepTrack() 
keeping_track.func() 
+4

¿Por qué no utilizar 'for _ in xrange (N)' para hacer algo N veces? Es conceptualmente más simple que abusar de un 'RuntimeError', permite llamar a otras funciones sin salir prematuramente (o, lo que es peor, requiere que la persona que llama se encargue de eso), y debería ser más eficiente como una bonificación. – delnan

+2

Cuando lo haces en main, ya estás en una función. Cuando estás en el intérprete, no lo eres. – agf

+0

¿Por qué querrías hacer esto? –

Respuesta

7

No debe cambiar el límite de recursión del sistema en absoluto. Debe codificar su función para saber qué tan profunda es y finalizar la recursión cuando sea demasiado profunda.

La razón por la cual el límite de recursividad se aplica de manera diferente en el programa y el intérprete se debe a que tienen diferentes tops de pila: las funciones invocadas en el intérprete para llegar al punto de ejecutar el código.

0

Definitivamente elegiría el primer enfoque, es más simple y auto explicativo. Después de todo el límite de recursión es su elección explícita, ¿por qué ofuscarlo?

1

Aunque algo tangencial (Me he puesto en un comentario, pero no creo que hay espacio), cabe señalar que setrecursionlimit es algo engañosamente nombrado - lo que realmente establece la profundidad máxima de pila:

http://docs.python.org/library/sys.html#sys.setrecursionlimit

Es por eso que la función se comporta de manera diferente dependiendo de dónde la llame. Además, si func2 hiciera una llamada a stdlib (o lo que sea) que terminara llamando a un número de funciones tal que añadiera más de N a la pila, la excepción se activaría temprano.

Además, tampoco cambiaría el sys.tracebacklimit; eso tendrá un efecto en el resto de su programa. Ve con la respuesta de Ned.

1

haciendo caso omiso de los problemas más generales, parece que puede obtener la profundidad de cuadro actual mirando la longitud de inspeccionar.getouterframes(). eso le daría un "punto cero" desde el cual puede establecer el límite de profundidad (descargo de responsabilidad: no lo he intentado).

editar: or len (inspect.stack()) - no está claro para mí cuál es la diferencia. Me interesaría saber si esto funciona, y si eran diferentes.

Cuestiones relacionadas