2014-04-09 10 views
8
def f1(): 
    f1() 

Sappiamo tutti che chiamare questa funzione in Python produrrà RuntimeError: maximum recursion depth exceededPython ricorsione RuntimeError

ho scritto la sua versione modificata sligtly:

def f2(): 
    try: 
     f2() #This line throws an error 
    finally: #except works too 
     f2() #This line does not throw an error! 

La seconda funzione viene eseguito sempre senza gettare RuntimeError. Per di più, non ero in grado di fermarlo con la combinazione CtrlC.

Non capisco perché chiamare f2() non lancia RuntimeError. Puoi spiegarlo per favore?

risposta

4

Come lo stack si riempie, si chiama f2 all'interno del try fino a raggiungere la massima profondità di ricorsione.

Una volta che è raggiunto che, solleva un RuntimeError, che viene gestita dal finally

Che a sua volta solleva la stessa RuntimeError, ma ora alla pila in precedenza, che passa lungo alla chiamata finally.

Al suo interno, supera nuovamente la profondità massima.

Quando viene sollevato uno KeyboardInterrupt, il programma si sposta ugualmente sullo finally e non esce.

Non funzionerà tecnicamente per sempre, perché c'è solo uno finally. Detto questo, (grazie ai commenti), consente in modo esponenziale più chiamate, che è abbastanza vicino all'infinito. Una profondità di ricorsione di 100 si trasformerebbe in 2 == 1267650600228229401496703205376.

Se ci sono voluti 1 ms per ogni chiamata, ci vorrebbero 465 miliardi anni per completare.E questo è solo una profondità di 100

+0

credo che questo finirà per essere 'O (n ** n)' nel numero massimo di frame dello stack. così, un po 'di tempo. – Eevee

+0

Stavo proprio venendo a questa realizzazione. Yikes! – mhlester

+0

o forse "O (2 ** n)", in realtà. astronomico in entrambi i casi :) – Eevee

4

L'eccezione è ancora in fase di gettata, ma prima di Python può mostrare si chiedono f2()nuovo.

Quindi ogni volta che viene sollevata l'eccezione, si introduce un'altra chiamata. Quella chiamata ricorsiva è consentita (perché siamo un gradino al di sotto del limite), superiamo il limite, l'eccezione viene nuovamente sollevata, il gestore finally si intrufola in un'altra chiamata, quasi ad infinitum.

CTRL-C non termina il programma per gli stessi motivi; viene sollevata un'eccezione (KeyboardInterrupt), ma di nuovo il gestore finally: ti riconduce in ricorsione.

Ora stai cadendo a una velocità tale che sei entrato in orbita attorno all'interprete.

Tutto fa fine, ma i finally gestori di aggiungere un numero esponenzialmente crescente di chiamate in più prima che la pila può pienamente rilassarsi:

>>> import sys 
>>> def f2(depth=0, final=0): 
...  try: 
...   print depth 
...   f2(depth + 1, final) 
...  finally: 
...   print 'finally:', final 
...   f2(depth, final + 1) 
... 
>>> sys.setrecursionlimit(5) 
>>> f2() 
0 
1 
2 
3 
finally: 0 
finally: 0 
2 
finally: 1 
finally: 0 
1 
2 
finally: 1 
finally: 1 
1 
finally: 2 
finally: 0 
0 
1 
2 
finally: 1 
finally: 1 
1 
finally: 2 
finally: 1 
0 
1 
finally: 2 
finally: 2 
0 
finally: 3 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 7, in f2 
    File "<stdin>", line 7, in f2 
    File "<stdin>", line 7, in f2 
    File "<stdin>", line 7, in f2 
RuntimeError: maximum recursion depth exceeded 
+1

Vorrei poter dare un altro +1 per 'Ora stai cadendo ad una velocità tale che sei entrato in orbita attorno all'interprete – inspectorG4dget

+0

Funzionerà per sempre o alla fine genererà un'eccezione (dopo MOLTO lungo tempo suggerito da @mhlester)? –

+0

@pbackup: verrà eseguito per sempre. C'è solo un limite di stack, non una radice quadrata di limiti. Continui 'a recuperare' e poi rientri nel problema riapplicando un gestore 'try'-'finally'. –

Problemi correlati