Pythonin rekursiorajan tarkistaminen ja muuttaminen (esim. sys.setrecursionlimit).

liiketoiminta

Pythonissa rekursioiden määrälle on yläraja (rekursioiden enimmäismäärä). Jos haluat suorittaa rekursiivisen funktion, jossa on suuri määrä kutsuja, on tarpeen muuttaa rajaa. Käytä standardikirjaston sys-moduulin funktioita.

Myös pinon koko rajoittaa rekursioiden määrää. Joissakin ympäristöissä voidaan käyttää standardikirjaston resurssimoduulia pinon maksimikoon muuttamiseen (se toimi Ubuntussa, mutta ei Windowsissa tai macissa).

Seuraavat tiedot annetaan tässä.

  • Hae nykyisen rekursioiden lukumäärän yläraja:sys.getrecursionlimit()
  • Muuta rekursioiden lukumäärän ylärajaa:sys.setrecursionlimit()
  • Muuta pinon enimmäiskokoa:resource.setrlimit()

Esimerkkikoodi on käynnissä Ubuntussa.

Hae nykyinen rekursiorajoitus: sys.getrecursionlimit()

Nykyinen rekursiorajoitus saadaan käyttämällä sys.getrecursionlimit().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

Esimerkissä rekursioiden enimmäismäärä on 1000, mikä voi vaihdella ympäristöstäsi riippuen. Huomaa, että tässä tuotua resurssia käytetään myöhemmin, mutta ei Windowsissa.

Esimerkkinä käytetään seuraavaa yksinkertaista rekursiivista funktiota. Jos argumenttina on positiivinen kokonaisluku n, kutsujen määrä on n kertaa.

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

Virheilmoitus (RecursionError) annetaan, jos yrität tehdä rekursiota yli ylärajan.

recu_test(950)
# Finish

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

Huomaa, että sys.getrecursionlimit():llä saatu arvo ei ole varsinaisesti rekursioiden maksimimäärä, vaan Python-tulkin pinon maksimisyvyys, joten vaikka rekursioiden määrä olisi hieman pienempi kuin tämä arvo, annetaan virheilmoitus (RecursionError).

Rekursioraja ei ole rekursioraja, vaan python-tulkin pinon enimmäissyvyys.
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

Muuta rekursiorajoitusta: sys.setrecursionlimit()

Rekursioiden lukumäärän ylärajaa voidaan muuttaa komennolla sys.setrecursionlimit(). Yläraja annetaan argumenttina.

Mahdollistaa syvemmän rekursion suorittamisen.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Jos määritetty yläraja on liian pieni tai liian suuri, tapahtuu virhe. Tämä rajoitus (itse rajan ylä- ja alaraja) vaihtelee ympäristön mukaan.

Raja-arvon enimmäisarvo riippuu alustasta. Jos tarvitset syvää rekursiota, voit määrittää suuremman arvon alustan tukemalta alueelta, mutta muista, että tämä arvo aiheuttaa kaatumisen, jos se on liian suuri.
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

Myös pinon koko rajoittaa rekursioiden enimmäismäärää, kuten seuraavassa selitetään.

Muuta pinon enimmäiskokoa: resource.setrlimit()

Vaikka sys.setrecursionlimit()-ohjelmassa asetetaan suuri arvo, sitä ei välttämättä suoriteta, jos rekursioiden määrä on suuri. Segmentointivirhe tapahtuu seuraavasti.

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

# recu_test(10 ** 5)
# Segmentation fault

Pythonissa standardikirjaston resurssimoduulia voidaan käyttää pinon maksimikoon muuttamiseen. Resource-moduuli on kuitenkin Unix-kohtainen moduuli, eikä sitä voi käyttää Windowsissa.

Käyttämällä resource.getrlimit() voit saada argumentissa määritetyn resurssin rajan (soft limit, hard limit) tuplina. Tässä määritetään resurssiksi resource.RLIMIT_STACK, joka edustaa nykyisen prosessin kutsupinon enimmäiskokoa.

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

Esimerkissä pehmeä raja on 8388608 (8388608 B = 8192 KB = 8 MB) ja kova raja on -1 (rajoittamaton).

Voit muuttaa resurssin rajaa käyttämällä resource.setrlimit(). Tässä myös pehmeä raja asetetaan arvoon -1 (ei rajaa). Voit myös käyttää vakiota resource.RLIM_INFINIT edustamaan rajoittamatonta rajaa.

Syvä rekursio, jota ei voitu suorittaa segmentointivian vuoksi ennen pinon koon muutosta, voidaan nyt suorittaa.

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

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

recu_test(10 ** 5)
# Finish

Tässä pehmeä raja on asetettu arvoon -1 (ei rajaa) yksinkertaisen kokeen vuoksi, mutta todellisuudessa olisi turvallisempaa rajoittaa se sopivaan arvoon.

Lisäksi, kun yritin asettaa rajoittamattoman pehmeän rajan myös macilleni, tuli seuraava virheilmoitus.ValueError: not allowed to raise maximum limit
Skriptin suorittaminen sudo-toiminnolla ei auttanut. Järjestelmä saattaa rajoittaa sitä.

Prosessi, jolla on superkäyttäjän todellinen UID, voi pyytää mitä tahansa kohtuullista rajoitusta, mukaan lukien ei rajoitusta.
Järjestelmän asettaman rajan ylittävä pyyntö johtaa kuitenkin edelleen ValueError-virheeseen.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

Windowsissa ei ole resurssimoduulia, eikä mac voinut muuttaa pinon enimmäiskokoa järjestelmärajoitusten vuoksi. Jos voimme kasvattaa pinon kokoa jollakin keinolla, meidän pitäisi pystyä ratkaisemaan segmentointivika, mutta emme ole pystyneet vahvistamaan tätä.

Copied title and URL