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.
- Unix Specific Services — Python 3.10.0 Documentation
- resource — Resource usage information — Python 3.10.0 Documentation
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.
- resource.getrlimit() — Resource usage information — Python 3.10.0 Documentation
- resource.RLIMIT_STACK — Resource usage information — Python 3.10.0 Documentation
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ä.