A Pythonban a rekurziók számának van egy felső határa (a rekurziók maximális száma). Egy rekurzív függvény nagyszámú hívással történő végrehajtásához szükséges a határérték megváltoztatása. Használja a szabványos könyvtár sys moduljában található függvényeket.
A rekurziók számát szintén a verem mérete korlátozza. Bizonyos környezetekben a szabványos könyvtár resource moduljával megváltoztatható a maximális veremméret (Ubuntun működött, de Windowson vagy mac-en nem).
A következő információk itt találhatók.
- Az aktuális rekurziók számának felső határértékének lekérdezése:
sys.getrecursionlimit()
- A rekurziók számának felső határának módosítása:
sys.setrecursionlimit()
- A verem maximális méretének módosítása:
resource.setrlimit()
A mintakód Ubuntun fut.
Az aktuális rekurziós limit lekérdezése: sys.getrecursionlimit()
Az aktuális rekurziós korlátot a sys.getrecursionlimit() paranccsal kaphatjuk meg.
import sys
import resource
print(sys.getrecursionlimit())
# 1000
A példában a rekurziók maximális száma 1000, ami a környezettől függően változhat. Vegye figyelembe, hogy az itt importált erőforrást később használni fogjuk, de nem Windowson.
Példaként a következő egyszerű rekurzív függvényt fogjuk használni. Ha argumentumként egy pozitív egész számot, n-t adunk meg, akkor a hívások száma n-szeres lesz.
def recu_test(n):
if n == 1:
print('Finish')
return
recu_test(n - 1)
Hiba (RecursionError) lép fel, ha a felső határnál több rekurziót próbál végrehajtani.
recu_test(950)
# Finish
# recu_test(1500)
# RecursionError: maximum recursion depth exceeded in comparison
Vegyük figyelembe, hogy a sys.getrecursionlimit() által kapott érték nem szigorúan a rekurziók maximális száma, hanem a Python-interpreter maximális veremmélysége, így még akkor is hiba (RecursionError) fog jelentkezni, ha a rekurziók száma valamivel kevesebb, mint ez az érték.
A rekurziós határ nem a rekurzió határa, hanem a python-interpreter veremének maximális mélysége.
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
Rekurziós korlát módosítása: sys.setrecursionlimit()
A rekurziók számának felső határa a sys.setrecursionlimit() paranccsal módosítható. A felső korlátot argumentumként kell megadni.
Lehetővé teszi a mélyebb rekurzió végrehajtását.
sys.setrecursionlimit(2000)
print(sys.getrecursionlimit())
# 2000
recu_test(1500)
# Finish
Ha a megadott felső határ túl kicsi vagy túl nagy, hiba lép fel. Ez a megkötés (maga a határérték felső és alsó határa) a környezettől függően változik.
A határérték maximális értéke a platformtól függ. Ha mély rekurzióra van szüksége, megadhat egy nagyobb értéket a platform által támogatott tartományon belül, de vegye figyelembe, hogy ez az érték összeomlást okoz, ha túl nagy.
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
A rekurziók maximális számát a verem mérete is korlátozza, amint azt a következőkben kifejtjük.
A verem maximális méretének módosítása: resource.setrlimit()
Még ha a sys.setrecursionlimit() nagy értéket is állít be, előfordulhat, hogy a rekurziók nagy száma esetén nem kerül végrehajtásra. A szegmentációs hiba a következőképpen következik be.
sys.setrecursionlimit(10 ** 9)
print(sys.getrecursionlimit())
# 1000000000
recu_test(10 ** 4)
# Finish
# recu_test(10 ** 5)
# Segmentation fault
Pythonban a szabványos könyvtárban található resource modul segítségével módosítható a maximális veremméret. Az erőforrásmodul azonban Unix-specifikus modul, és Windows alatt nem használható.
- Unix Specific Services — Python 3.10.0 Documentation
- resource — Resource usage information — Python 3.10.0 Documentation
A resource.getrlimit() segítségével az argumentumban megadott erőforrás határértékét a (soft limit, hard limit) tupliként kaphatjuk meg. Itt az resource.RLIMIT_STACK erőforrásként adjuk meg, amely az aktuális folyamat híváshalmazának maximális méretét jelenti.
- 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)
A példában a puha korlát 8388608 (8388608 B = 8192 KB = 8 MB), a kemény korlát pedig -1 (korlátlan).
Az erőforrás határértékét a resource.setrlimit() segítségével módosíthatja. Itt a soft limit is -1-re van állítva (nincs limit). A korlátlan korlátot a resource.RLIM_INFINIT konstanssal is megjeleníthetjük.
A mély rekurzió, amelyet a veremméret változása előtt a szegmentációs hiba miatt nem lehetett végrehajtani, most már elvégezhető.
resource.setrlimit(resource.RLIMIT_STACK, (-1, -1))
print(resource.getrlimit(resource.RLIMIT_STACK))
# (-1, -1)
recu_test(10 ** 5)
# Finish
Itt a lágy határérték -1-re van állítva (nincs határérték) egy egyszerű kísérlethez, de a valóságban biztonságosabb lenne egy megfelelő értékre korlátozni.
Ezen kívül, amikor megpróbáltam korlátlan soft limitet beállítani a mac-en is, a következő hiba lépett fel.ValueError: not allowed to raise maximum limit
A szkript sudo-val történő futtatása nem segített. Lehet, hogy a rendszer korlátozza.
A szuperfelhasználó tényleges UID-jével rendelkező folyamat bármilyen ésszerű korlátozást kérhet, beleértve a korlátozás nélküli korlátozást is.
A rendszer által meghatározott korlátot túllépő kérés azonban továbbra is ValueError-t eredményez.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation
A Windows nem rendelkezik erőforrás-modullal, és a mac a rendszerkorlátozások miatt nem tudta megváltoztatni a maximális veremméretet. Ha valamilyen módon növelni tudjuk a veremméretet, akkor meg kellene oldani a szegmentációs hibát, de ezt nem tudtuk megerősíteni.