A Python rekurziós korlátjának ellenőrzése és módosítása (pl. sys.setrecursionlimit)

Üzleti

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ó.

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.

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.

Copied title and URL