Kontroller og ændr Pythons rekursionsgrænse (f.eks. sys.setrecursionlimit)

Forretning

I Python er der en øvre grænse for antallet af rekursioner (det maksimale antal rekursioner). Hvis du vil udføre en rekursiv funktion med et stort antal kald, er det nødvendigt at ændre grænsen. Brug funktionerne i sys-modulet i standardbiblioteket.

Antallet af rekursioner er også begrænset af stakkens størrelse. I nogle miljøer kan ressourcemodulet i standardbiblioteket bruges til at ændre den maksimale stakkestørrelse (det virkede på Ubuntu, men ikke på Windows eller mac).

Her findes følgende oplysninger.

  • Få den øvre grænse for det aktuelle antal rekursioner:sys.getrecursionlimit()
  • Ændre den øvre grænse for antallet af rekursioner:sys.setrecursionlimit()
  • Ændre den maksimale størrelse af stakken:resource.setrlimit()

Prøvekoden kører på Ubuntu.

Få den aktuelle rekursionsgrænse: sys.getrecursionlimit()

Den aktuelle rekursionsgrænse kan fås med sys.getrecursionlimit().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

I eksemplet er det maksimale antal rekursioner 1000, men det kan variere afhængigt af dit miljø. Bemærk, at den ressource, som vi importerer her, vil blive brugt senere, men ikke på Windows.

Som eksempel bruger vi følgende simple rekursive funktion. Hvis der angives et positivt heltal n som argument, vil antallet af kald være n gange.

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

Der opstår en fejl (RecursionError), hvis du forsøger at udføre rekursion mere end den øvre grænse.

recu_test(950)
# Finish

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

Bemærk, at den værdi, der opnås ved sys.getrecursionlimit(), ikke strengt taget er det maksimale antal rekursioner, men den maksimale stakkedybde for Python-fortolkeren, så selv hvis antallet af rekursioner er lidt mindre end denne værdi, vil der blive givet en fejl (RecursionError).

Rekursionsgrænsen er ikke grænsen for rekursion, men den maksimale dybde af stakken i python-fortolkeren.
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

Ændring af rekursionsgrænsen: sys.setrecursionlimit()

Den øvre grænse for antallet af rekursioner kan ændres ved hjælp af sys.setrecursionlimit(). Den øvre grænse angives som et argument.

Gør det muligt at foretage dybere rekursion.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Hvis den angivne øvre grænse er for lille eller for stor, opstår der en fejl. Denne begrænsning (den øvre og nedre grænse for selve grænsen) varierer afhængigt af miljøet.

Den maksimale værdi af grænsen afhænger af platformen. Hvis du har brug for dyb rekursion, kan du angive en større værdi inden for det område, der understøttes af platformen, men vær opmærksom på, at denne værdi vil forårsage et nedbrud, hvis den er for stor.
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

Det maksimale antal rekursioner er også begrænset af stakkens størrelse, som forklaret i det følgende.

Ændre den maksimale størrelse af stakken: resource.setrlimit()

Selv om der er angivet en stor værdi i sys.setrecursionlimit(), kan det være, at den ikke bliver udført, hvis antallet af rekursioner er stort. En segmenteringsfejl opstår som følger.

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

# recu_test(10 ** 5)
# Segmentation fault

I Python kan ressourcemodulet i standardbiblioteket bruges til at ændre den maksimale stakkestørrelse. Ressourcemodulet er dog et Unix-specifikt modul og kan ikke bruges på Windows.

Med resource.getrlimit() kan du hente grænsen for den ressource, der er angivet i argumentet, som en tupel af (blød grænse, hård grænse). Her angiver vi resource.RLIMIT_STACK som ressource, som repræsenterer den maksimale størrelse af den aktuelle process' call stack.

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

I eksemplet er den bløde grænse 8388608 (8388608 B = 8192 KB = 8 MB), og den hårde grænse er -1 (ubegrænset).

Du kan ændre ressourcens grænse med resource.setrlimit(). Her er den bløde grænse også sat til -1 (ingen grænse). Du kan også bruge konstanten resource.RLIM_INFINIT til at repræsentere den ubegrænsede grænse.

Dyb rekursion, som ikke kunne udføres på grund af segmenteringsfejl før ændringen af stakkens størrelse, kan nu udføres.

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

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

recu_test(10 ** 5)
# Finish

Her er den bløde grænse sat til -1 (ingen grænse) for et simpelt eksperiment, men i virkeligheden ville det være mere sikkert at begrænse den til en passende værdi.

Da jeg desuden forsøgte at indstille en ubegrænset soft limit på min mac, opstod følgende fejl.ValueError: not allowed to raise maximum limit
Det hjalp ikke at køre scriptet med sudo. Det kan være begrænset af systemet.

En proces med en superbrugerens effektive UID kan anmode om enhver rimelig begrænsning, herunder ingen begrænsning.
En anmodning, der overskrider den grænse, som systemet har fastsat, vil dog stadig resultere i en ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

Windows har ikke et ressourcemodul, og mac kunne ikke ændre den maksimale stakkestørrelse på grund af systembegrænsninger. Hvis vi kan øge stakkens størrelse på en eller anden måde, burde vi kunne løse segmenteringsfejlen, men vi har ikke kunnet bekræfte dette.

Copied title and URL