Proverite i promenite Python ograničenje rekurzije (npr. sys.setrecursionlimit)

Бусинесс

U Python-u postoji gornja granica za broj rekurzija (maksimalni broj rekurzija). Da biste izvršili rekurzivnu funkciju sa velikim brojem poziva, potrebno je promeniti ograničenje. Koristite funkcije u sys modulu standardne biblioteke.

Broj rekurzija je takođe ograničen veličinom steka. U nekim okruženjima, modul resursa standardne biblioteke može da se koristi za promenu maksimalne veličine steka (radio je na Ubuntu-u, ali ne i na Windows-u ili Mac-u).

Овде су наведене следеће информације.

  • Dobijte gornju granicu trenutnog broja rekurzija:sys.getrecursionlimit()
  • Promenite gornju granicu broja rekurzija:sys.setrecursionlimit()
  • Promenite maksimalnu veličinu steka:resource.setrlimit()

Primer koda radi na Ubuntu-u.

Dobijte trenutno ograničenje rekurzije: sys.getrecursionlimit()

Trenutna granica rekurzije se može dobiti pomoću sys.getrecursionlimit().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

U primeru, maksimalan broj rekurzija je 1000, što može da varira u zavisnosti od vašeg okruženja. Imajte na umu da će resurs koji uvozimo ovde biti korišćen kasnije, ali ne na Windows-u.

Kao primer, koristićemo sledeću jednostavnu rekurzivnu funkciju. Ako je pozitivan ceo broj n naveden kao argument, broj poziva će biti n puta.

def recu_test(n):
    if n == 1:
        print('Finish')
        return
    recu_test(n - 1)

Greška (RecursionError) će se pojaviti ako pokušate da izvršite rekurziju više od gornje granice.

recu_test(950)
# Finish

# recu_test(1500)
# RecursionError: maximum recursion depth exceeded in comparison

Imajte na umu da vrednost dobijena pomoću sys.getrecursionlimit() nije striktno maksimalan broj rekurzija, već maksimalna dubina steka Python interpretatora, pa čak i ako je broj rekurzija nešto manji od ove vrednosti, greška (RecursionError) će бити одгојен.

再帰限界は、再帰の限界ではなく、pythonインタープリタのスタックで最夯で最大
python – Max recursion is not exactly what sys.getrecursionlimit() claims. How come? – Stack Overflow

# recu_test(995)
# RecursionError: maximum recursion depth exceeded while calling a Python object

Promenite ograničenje rekurzije: sys.setrecursionlimit()

Gornja granica broja rekurzija se može promeniti pomoću sys.setrecursionlimit(). Gornja granica je navedena kao argument.

Omogućava izvođenje dublje rekurzije.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Ako je navedena gornja granica premala ili prevelika, pojaviće se greška. Ovo ograničenje (gornja i donja granica same granice) varira u zavisnosti od okruženja.

Maksimalna vrednost ograničenja zavisi od platforme. Ako vam je potrebna duboka rekurzija, možete navesti veću vrednost unutar opsega koji podržava platforma, ali imajte na umu da će ova vrednost izazvati pad ako je prevelika.
If the new limit is too low at the current recursion depth, a RecursionError exception is raised.
sys.setrecursionlimit() — System-specific parameters and functions — Python 3.10.0 Documentation

sys.setrecursionlimit(4)
print(sys.getrecursionlimit())
# 4

# sys.setrecursionlimit(3)
# RecursionError: cannot set the recursion limit to 3 at the recursion depth 1: the limit is too low

sys.setrecursionlimit(10 ** 9)
print(sys.getrecursionlimit())
# 1000000000

# sys.setrecursionlimit(10 ** 10)
# OverflowError: signed integer is greater than maximum

Maksimalan broj rekurzija je takođe ograničen veličinom steka, kao što je objašnjeno u nastavku.

Promenite maksimalnu veličinu steka: resource.setrlimit()

Čak i ako je velika vrednost postavljena u sys.setrecursionlimit(), možda neće biti izvršena ako je broj rekurzija veliki. Greška segmentacije se javlja na sledeći način.

sys.setrecursionlimit(10 ** 9)
print(sys.getrecursionlimit())
# 1000000000
recu_test(10 ** 4)
# Finish

# recu_test(10 ** 5)
# Segmentation fault

U Python-u, modul resursa u standardnoj biblioteci se može koristiti za promenu maksimalne veličine steka. Međutim, modul resursa je modul specifičan za Unix i ne može se koristiti u Windows-u.

Pomoću resource.getrlimit(), možete dobiti ograničenje resursa navedenog u argumentu kao tuple of (meko ograničenje, tvrdo ograničenje). Ovde navodimo resource.RLIMIT_STACK kao resurs, koji predstavlja maksimalnu veličinu steka poziva trenutnog procesa.

print(resource.getrlimit(resource.RLIMIT_STACK))
# (8388608, -1)

U primeru, meko ograničenje je 8388608 (8388608 B = 8192 KB = 8 MB), a tvrdo ograničenje je -1 (neograničeno).

Možete promeniti ograničenje resursa pomoću resource.setrlimit(). Ovde je meka granica takođe podešena na -1 (bez ograničenja). Takođe možete da koristite konstantni resurs.RLIM_INFINIT da predstavljate neograničeno ograničenje.

Duboka rekurzija, koja nije mogla da se izvrši zbog greške segmentacije pre promene veličine steka, sada može da se izvede.

resource.setrlimit(resource.RLIMIT_STACK, (-1, -1))

print(resource.getrlimit(resource.RLIMIT_STACK))
# (-1, -1)

recu_test(10 ** 5)
# Finish

Ovde je meko ograničenje postavljeno na -1 (bez ograničenja) za jednostavan eksperiment, ali u stvarnosti bi bilo sigurnije ograničiti ga na odgovarajuću vrednost.

Pored toga, kada sam pokušao da postavim neograničeno meko ograničenje i na svom Mac-u, dogodila se sledeća greška.ValueError: not allowed to raise maximum limit
Pokretanje skripte sa sudo nije pomoglo. To može biti ograničeno sistemom.

Proces sa efektivnim UID-om superkorisnika može zahtevati bilo koje razumno ograničenje, uključujući bez ograničenja.
Međutim, zahtev koji premašuje ograničenje koje je sistem nametnuo i dalje će rezultirati ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

Windows nema modul resursa, a Mac nije mogao da promeni maksimalnu veličinu steka zbog sistemskih ograničenja. Ako možemo da povećamo veličinu steka na neki način, trebalo bi da možemo da rešimo grešku segmentacije, ali to nismo mogli da potvrdimo.