Controleer en verander de Python recursie limiet (b.v. sys.setrecursionlimit)

Bedrijf

In Python is er een bovengrens aan het aantal recursies (het maximum aantal recursies). Om een recursieve functie met een groot aantal aanroepen uit te voeren, is het nodig om de limiet te veranderen. Gebruik de functies in de sys-module van de standaardbibliotheek.

Het aantal recursies wordt ook beperkt door de stack grootte. In sommige omgevingen kan de resource module van de standaard bibliotheek gebruikt worden om de maximale stack grootte te veranderen (het werkte op Ubuntu, maar niet op Windows of mac).

De volgende informatie wordt hier verstrekt.

  • Verkrijg de bovengrens van het huidige aantal recursies:sys.getrecursionlimit()
  • Wijzig de bovengrens van het aantal recursies:sys.setrecursionlimit()
  • Wijzig de maximumgrootte van de stapel:resource.setrlimit()

De voorbeeldcode draait op Ubuntu.

Verkrijg de huidige recursie limiet: sys.getrecursionlimit()

De huidige recursielimiet kan worden verkregen met sys.getrecursionlimit().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

In het voorbeeld is het maximum aantal recursies 1000, wat kan variëren afhankelijk van uw omgeving. Merk op dat de bron die we hier importeren later gebruikt zal worden, maar niet op Windows.

Als voorbeeld gebruiken we de volgende eenvoudige recursieve functie. Indien een positief geheel getal n als argument wordt opgegeven, zal het aantal aanroepen n keer zijn.

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

Er wordt een foutmelding (RecursionError) gegeven als u probeert meer recursies uit te voeren dan de bovengrens.

recu_test(950)
# Finish

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

Merk op dat de waarde verkregen door sys.getrecursionlimit() strikt genomen niet het maximum aantal recursies is, maar de maximum stack diepte van de Python interpreter, dus zelfs als het aantal recursies iets minder is dan deze waarde, zal er een fout (RecursionError) worden opgewekt.

De recursie limiet is niet de limiet van recursie, maar de maximale diepte van de stack van de python interpreter.
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

Verander recursie limiet: sys.setrecursionlimit()

De bovengrens van het aantal recursies kan worden veranderd met sys.setrecursionlimit(). De bovengrens wordt als argument opgegeven.

Hiermee kan diepere recursie worden uitgevoerd.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Als de opgegeven bovengrens te klein of te groot is, treedt een fout op. Deze beperking (boven- en ondergrens van de limiet zelf) varieert afhankelijk van de omgeving.

De maximale waarde van limiet hangt af van het platform. Als je diepe recursie nodig hebt, kun je een grotere waarde opgeven binnen het bereik dat door het platform wordt ondersteund, maar wees je ervan bewust dat deze waarde een crash zal veroorzaken als hij te groot is.
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

Het maximum aantal recursies wordt ook beperkt door de grootte van de stack, zoals hierna wordt uitgelegd.

Wijzig de maximale grootte van de stack: resource.setrlimit()

Zelfs als een grote waarde is ingesteld in sys.setrecursionlimit(), kan het zijn dat het niet wordt uitgevoerd als het aantal recursies groot is. Een segmentatiefout treedt als volgt op.

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

# recu_test(10 ** 5)
# Segmentation fault

In Python kan de resource-module in de standaardbibliotheek worden gebruikt om de maximale stack-grootte te wijzigen. De resource module is echter een Unix-specifieke module en kan niet onder Windows worden gebruikt.

Met resource.getrlimit() kan je de limiet van de in het argument gespecificeerde resource krijgen als een tupel van (soft limit, hard limit). Hier specificeren we resource.RLIMIT_STACK als de resource, die de maximale grootte van de aanroep-stack van het huidige proces weergeeft.

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

In het voorbeeld is de zachte limiet 8388608 (8388608 B = 8192 KB = 8 MB) en de harde limiet is -1 (onbeperkt).

Je kunt de limiet van de bron veranderen met resource.setrlimit(). Hier wordt de zachte limiet ook ingesteld op -1 (geen limiet). Je kunt ook de constante resource.RLIM_INFINIT gebruiken om de ongelimiteerde limiet weer te geven.

Diepe recursie, die niet kon worden uitgevoerd wegens segmentatiefouten vóór de wijziging van de stackgrootte, kan nu wel worden uitgevoerd.

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

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

recu_test(10 ** 5)
# Finish

Hier is de zachte limiet ingesteld op -1 (geen limiet) voor een eenvoudig experiment, maar in werkelijkheid zou het veiliger zijn deze te beperken tot een passende waarde.

Bovendien, toen ik probeerde om ook op mijn mac een onbeperkte soft limiet in te stellen, trad de volgende fout op.ValueError: not allowed to raise maximum limit
Het script uitvoeren met sudo hielp niet. Het kan door het systeem beperkt zijn.

Een proces met de effectieve UID van een supergebruiker kan elke redelijke limiet aanvragen, inclusief geen limiet.
Een verzoek dat de door het systeem opgelegde limiet overschrijdt, zal echter nog steeds resulteren in een ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

Windows heeft geen resource module, en mac kon de maximale stack-grootte niet veranderen vanwege systeembeperkingen. Als we de stack op de een of andere manier kunnen vergroten, zouden we in staat moeten zijn om de segmentatiefout op te lossen, maar we hebben dit niet kunnen bevestigen.