Tengo 2 soluciones a 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 alterar el límite de recursión). ¿Alguien puede pensar en una mejor manera de hacer esto o recomendar el uso de 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 rastreo, pero puede haber advertencias.

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()

Sin embargo, veo un comportamiento extraño con el administrador de contexto. Si pongo en main

with recursion_limit(12):
    func2()

Imprime del 1 al 10. Si hago lo mismo desde el intérprete, imprime del 1 al 11. ¿Asumo que sucede algo debajo del capó cuando importo cosas?

EDITAR: Para la posteridad, esto es lo que se me ocurrió para una función que conoce su profundidad de llamada. Dudo que lo use en cualquier código de producción, pero hace el trabajo.

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()
11
jseabold 4 ago. 2011 a las 21:08

4 respuestas

La mejor respuesta

No debe cambiar el límite de recurrencia 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 recursión parece aplicarse de manera diferente en su programa y en el intérprete es porque tienen diferentes partes superiores de la pila: las funciones invocadas en el intérprete para llegar al punto de ejecutar su código.

7
Ned Batchelder 4 ago. 2011 a las 17:20

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

0
Nicola Musatti 4 ago. 2011 a las 17:17

Haciendo caso omiso de los problemas más generales, parece que puede obtener la profundidad de cuadro actual mirando la longitud de inspect.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).

Edit: or len (inspect.stack ()): no me queda claro cuál es la diferencia. Me interesaría saber si esto funciona y si eran diferentes.

1
andrew cooke 4 ago. 2011 a las 17:48

Si bien es algo tangencial (lo habría puesto en un comentario, pero no creo que haya espacio), debe tenerse en cuenta que setrecursionlimit tiene un nombre algo engañoso: en realidad establece la profundidad máxima de la 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 stdlib (o lo que sea) que terminara llamando a una serie de funciones de modo que agregara más de N a la pila, la excepción se dispararía antes.

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
Eli Stevens 4 ago. 2011 a las 17:27