2012-06-27 14 views
6

Dlaczego/jak to stworzyć pozornie nieskończoną pętlę? Niepoprawnie, zakładałem, że spowoduje to jakąś formę błędu typu przepełnienia stosu.Dlaczego ten skrypt w języku Python tworzy nieskończoną pętlę? (Rekurencja)

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

Cóż, po prostu zachować na wywołanie 'foo', bez warunku zatrzymania, więc nadal będzie rekursja wiecznie. Nawet jeśli otrzymasz wyjątek, powtarzasz _again_. –

+0

Czy w Pythonie nie powinno zabraknąć pamięci? Czy też stos wywołający jest wyczyszczony po wystąpieniu "RuntimeError"? – rectangletangle

+0

Ewentualnie pyton optymalizuje go do iteracji – Blorgbeard

Odpowiedz

4

W przypadku zmiany kodu do

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

Będziesz zauważyć, że wyjście oscyluje skrócie poniżej 999 (1000 jest domyślny próg rekurencji Pythona). Oznacza to, że gdy granica jest trafiony (RuntimeError), że w ubiegłym wezwanie foo() jest zakończona, a drugi jest wyłączony, aby go natychmiast wymienić.

Jeśli podnieść KeyboardInterrupt będziesz obserwować, jak cała ślad jest zakończony na raz.


UPDATE

ciekawe drugie wezwanie foo() nie jest chroniony przez try ... except -blok więcej. W związku z tym wniosek ostatecznie zostanie rozwiązany. To staje się widoczne, jeśli ustawisz limit rekursji na mniejszą liczbę, np. wyjście dla 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

Wyjątkiem RuntimeError zostanie zwiększona, jeżeli zostanie przekroczona wartość graniczna rekursji.

Skoro jesteś połowu ten wyjątek, urządzenie będzie dalej, ale jesteś dodając tylko do jednej globalnej wartości int, która nie używa dużo pamięci.

Można ustawić limit rekurencji z sys.setrecursionlimit(). Bieżący limit można znaleźć pod numerem 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 
>>> 

Jeśli chcesz zabraknąć pamięci spróbuj jej więcej.

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