2012-06-27 5 views
6

¿Por qué/cómo esto crea un bucle aparentemente infinito? Incorrectamente, supuse que esto causaría algún tipo de error de tipo desbordamiento de pila.¿Por qué este script de Python crea un bucle infinito? (recursión)

i = 0 

def foo() : 
    global i 

    i += 1 
    try : 
     foo() 
    except RuntimeError : 
     # This call recursively goes off toward infinity, apparently. 
     foo() 

foo() 

print i 
+5

Bueno, sólo sigue llamando 'foo', sin criterio de parada, por lo que seguirá recursivo para siempre. E incluso cuando obtienes una excepción, recurres _again_. –

+0

¿No debería quedarse sin memoria en Python o algo así? ¿O se borra la pila de llamadas después del 'RuntimeError'? – rectangletangle

+0

Posiblemente python lo está optimizando en la iteración – Blorgbeard

Respuesta

4

Si cambia el código para

i = 0 
def foo(): 
    global i 
    i += 1 
    print i 
    try : 
     foo() 
    except RuntimeError : 
     # This call recursively goes off toward infinity, apparently. 
     foo() 
    finally: 
     i -= 1 
     print i 

foo() 

usted observará que la salida oscila corta por debajo de 999 (1000 siendo límite de recursividad por defecto de Python). Esto significa que cuando se alcanza el límite (RuntimeError) finaliza la última llamada de foo() y se activa otra para reemplazarla de inmediato.

Si levanta un KeyboardInterrupt, observará cómo se termina toda la traza a la vez.


ACTUALIZACIÓN

Curiosamente la segunda llamada de foo() no está protegido por el try ... except -bloque más. Por lo tanto, la aplicación de hecho terminará eventualmente. Esto se convierte en adecuado si establece el límite de recursión en un número más pequeño, p. la salida de sys.setrecursionlimit(3):

$ python test.py 
1 
2 
1 
2 
1 
0 
Traceback (most recent call last): 
    File "test.py", line 19, in <module> 
    foo() 
    File "test.py", line 14, in foo 
    foo() 
    File "test.py", line 14, in foo 
    foo() 
RuntimeError 
6

El RuntimeError excepción serán levantados si se supera el límite de recursión.

Como está detectando esta excepción, su máquina continuará, pero solo está agregando a un solo valor global global, que no usa mucha memoria.

Puede establecer el límite de recursión con sys.setrecursionlimit(). El límite actual se puede encontrar con sys.getrecursionlimit().

>>> import sys 
>>> sys.setrecursionlimit(100) 
>>> 
>>> def foo(i): 
...  i += 1 
...  foo(i) 
... 
>>> foo(1) 
Traceback (most recent call last): 
    ... 
    File "<stdin>", line 3, in foo 
RuntimeError: maximum recursion depth exceeded 
>>> 

Si quiere quedarse sin memoria, intente consumir más cantidad.

>>> def foo(l): 
...     l = l * 100 
...     foo(l) 
... 
>>> foo(["hello"]) 
Traceback (most recent call last): 
    ... 
  File "<stdin>", line 2, in foo 
MemoryError 
>>> 
Cuestiones relacionadas