Kontrola a zmena limitu rekurzie v jazyku Python (napr. sys.setrecursionlimit)

obchodné

V jazyku Python existuje horný limit počtu rekurzií (maximálny počet rekurzií). Ak chcete vykonať rekurzívnu funkciu s veľkým počtom volaní, je potrebné tento limit zmeniť. Použite funkcie v module sys štandardnej knižnice.

Počet rekurzií je tiež obmedzený veľkosťou zásobníka. V niektorých prostrediach možno použiť modul zdrojov štandardnej knižnice na zmenu maximálnej veľkosti zásobníka (fungovalo to v Ubuntu, ale nie vo Windows alebo Macu).

Tu sú uvedené tieto informácie.

  • Získanie hornej hranice aktuálneho počtu rekurzií:sys.getrecursionlimit()
  • Zmena hornej hranice počtu rekurzií:sys.setrecursionlimit()
  • Zmena maximálnej veľkosti zásobníka:resource.setrlimit()

Ukážka kódu je spustená v systéme Ubuntu.

Získanie aktuálneho limitu rekurzie: sys.getrecursionlimit()

Aktuálny limit rekurzie možno získať pomocou funkcie sys.getrecursionlimit().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

V príklade je maximálny počet rekurzií 1000, čo sa môže líšiť v závislosti od prostredia. Všimnite si, že prostriedok, ktorý tu importujeme, sa bude používať neskôr, ale nie v systéme Windows.

Ako príklad použijeme nasledujúcu jednoduchú rekurzívnu funkciu. Ak je ako argument zadané celé kladné číslo n, počet volaní bude n-krát.

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

Ak sa pokúsite vykonať viac rekurzií, ako je horná hranica, zobrazí sa chyba (RecursionError).

recu_test(950)
# Finish

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

Všimnite si, že hodnota získaná pomocou sys.getrecursionlimit() nie je striktne maximálny počet rekurzií, ale maximálna hĺbka zásobníka interpretu jazyka Python, takže aj keď je počet rekurzií o niečo menší ako táto hodnota, vyskytne sa chyba (RecursionError).

Limit rekurzie nie je limit rekurzie, ale maximálna hĺbka zásobníka interpretu jazyka 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

Zmena limitu rekurzie: sys.setrecursionlimit()

Hornú hranicu počtu rekurzií možno zmeniť pomocou funkcie sys.setrecursionlimit(). Horný limit sa zadáva ako argument.

Umožňuje vykonať hlbšiu rekurziu.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Ak je zadaná horná hranica príliš malá alebo príliš veľká, dôjde k chybe. Toto obmedzenie (horná a dolná hranica samotného limitu) sa mení v závislosti od prostredia.

Maximálna hodnota limitu závisí od platformy. Ak potrebujete hlbokú rekurziu, môžete zadať väčšiu hodnotu v rámci rozsahu podporovaného platformou, ale uvedomte si, že táto hodnota spôsobí pád, ak je príliš veľká.
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

Maximálny počet rekurzií je tiež obmedzený veľkosťou zásobníka, ako je vysvetlené ďalej.

Zmena maximálnej veľkosti zásobníka: resource.setrlimit()

Aj keď je v príkaze sys.setrecursionlimit() nastavená veľká hodnota, nemusí sa vykonať, ak je počet rekurzií veľký. Segmentačná chyba nastane nasledovne.

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

# recu_test(10 ** 5)
# Segmentation fault

V jazyku Python možno na zmenu maximálnej veľkosti zásobníka použiť modul resource v štandardnej knižnici. Modul resource je však modul špecifický pre Unix a nie je možné ho použiť v systéme Windows.

Pomocou resource.getrlimit() môžete získať limit prostriedku zadaný v argumente ako tuple (soft limit, hard limit). Tu sme ako zdroj zadali resource.RLIMIT_STACK, ktorý predstavuje maximálnu veľkosť zásobníka volaní aktuálneho procesu.

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

V príklade je mäkký limit 8388608 (8388608 B = 8192 KB = 8 MB) a tvrdý limit je -1 (neobmedzený).

Limit zdroja môžete zmeniť pomocou resource.setrlimit(). Tu je mäkký limit tiež nastavený na -1 (žiadny limit). Môžete tiež použiť konštantu resource.RLIM_INFINIT, ktorá predstavuje neobmedzený limit.

Hlbokú rekurziu, ktorú nebolo možné vykonať kvôli poruche segmentácie pred zmenou veľkosti zásobníka, je teraz možné vykonať.

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

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

recu_test(10 ** 5)
# Finish

Tu je mäkký limit nastavený na -1 (žiadny limit) pre jednoduchý experiment, ale v skutočnosti by bolo bezpečnejšie obmedziť ho na vhodnú hodnotu.

Okrem toho, keď som sa pokúsil nastaviť neobmedzený mäkký limit aj na mojom počítači Mac, vyskytla sa nasledujúca chyba.ValueError: not allowed to raise maximum limit
Spustenie skriptu so sudo nepomohlo. Môže to byť obmedzené systémom.

Proces s efektívnym UID superpoužívateľa môže požadovať akýkoľvek primeraný limit vrátane žiadneho limitu.
Požiadavka, ktorá prekročí limit stanovený systémom, však bude mať za následok chybu ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

Systém Windows nemá modul prostriedkov a systém mac nemohol zmeniť maximálnu veľkosť zásobníka kvôli obmedzeniam systému. Ak sa nám podarí nejakým spôsobom zväčšiť veľkosť zásobníka, mali by sme byť schopní vyriešiť segmentačnú chybu, ale nepodarilo sa nám to potvrdiť.

Copied title and URL