I Python finns det en övre gräns för antalet rekursioner (maximalt antal rekursioner). För att utföra en rekursiv funktion med ett stort antal anrop är det nödvändigt att ändra gränsen. Använd funktionerna i sys-modulen i standardbiblioteket.
Antalet rekursioner begränsas också av stackstorleken. I vissa miljöer kan resursmodulen i standardbiblioteket användas för att ändra den maximala stackstorleken (det fungerade i Ubuntu, men inte i Windows eller mac).
Här finns följande information.
- Hämta den övre gränsen för det aktuella antalet rekursioner:
sys.getrecursionlimit()
- Ändra den övre gränsen för antalet rekursioner:
sys.setrecursionlimit()
- Ändra den maximala storleken på stapeln:
resource.setrlimit()
Kodprovet körs på Ubuntu.
Hämta den aktuella rekursionsgränsen: sys.getrecursionlimit()
Den aktuella rekursionsgränsen kan fås med sys.getrecursionlimit().
import sys
import resource
print(sys.getrecursionlimit())
# 1000
I exemplet är det maximala antalet rekursioner 1000, vilket kan variera beroende på din miljö. Observera att den resurs som vi importerar här kommer att användas senare, men inte i Windows.
Som exempel använder vi följande enkla rekursiva funktion. Om ett positivt heltal n anges som argument kommer antalet anrop att vara n gånger.
def recu_test(n):
if n == 1:
print('Finish')
return
recu_test(n - 1)
Ett fel (RecursionError) kommer att uppstå om du försöker utföra rekursion mer än den övre gränsen.
recu_test(950)
# Finish
# recu_test(1500)
# RecursionError: maximum recursion depth exceeded in comparison
Observera att det värde som fås med sys.getrecursionlimit() inte är det maximala antalet rekursioner, utan det maximala stackdjupet för Python-tolken, så även om antalet rekursioner är något mindre än detta värde kommer ett fel (RecursionError) att uppstå.
Rekursionsgränsen är inte gränsen för rekursion, utan det maximala djupet på stacken i Python-tolken.
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
Ändra gränsen för rekursion: sys.setrecursionlimit()
Den övre gränsen för antalet rekursioner kan ändras med sys.setrecursionlimit(). Den övre gränsen anges som ett argument.
Gör det möjligt att utföra djupare rekursion.
sys.setrecursionlimit(2000)
print(sys.getrecursionlimit())
# 2000
recu_test(1500)
# Finish
Om den angivna övre gränsen är för liten eller för stor uppstår ett fel. Denna begränsning (övre och undre gräns för själva gränsen) varierar beroende på miljön.
Det maximala värdet för gränsen beror på plattformen. Om du behöver djup rekursion kan du ange ett större värde inom det intervall som stöds av plattformen, men tänk på att värdet kommer att orsaka en krasch om det är för stort.
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 maximala antalet rekursioner begränsas också av stackstorleken, vilket förklaras nedan.
Ändra stackens maximala storlek: resource.setrlimit()
Även om ett stort värde anges i sys.setrecursionlimit() kan det hända att den inte kan utföras om antalet återkommande åtgärder är stort. Ett segmenteringsfel uppstår enligt följande.
sys.setrecursionlimit(10 ** 9)
print(sys.getrecursionlimit())
# 1000000000
recu_test(10 ** 4)
# Finish
# recu_test(10 ** 5)
# Segmentation fault
I Python kan resursmodulen i standardbiblioteket användas för att ändra den maximala stackstorleken. Resursmodulen är dock en Unix-specifik modul och kan inte användas i Windows.
- Unix Specific Services — Python 3.10.0 Documentation
- resource — Resource usage information — Python 3.10.0 Documentation
Med resource.getrlimit() kan du få fram gränsen för den resurs som anges i argumentet som en tupel av (mjuk gräns, hård gräns). Här anger vi resource.RLIMIT_STACK som resurs, som representerar den maximala storleken på den aktuella processens anropsstack.
- 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)
I exemplet är den mjuka gränsen 8388608 (8388608 B = 8192 KB = 8 MB) och den hårda gränsen är -1 (obegränsad).
Du kan ändra resursens gräns med resource.setrlimit(). Här är den mjuka gränsen också satt till -1 (ingen gräns). Du kan också använda konstanten resource.RLIM_INFINIT för att representera den obegränsade gränsen.
Djup rekursion, som inte kunde utföras på grund av segmenteringsfel före ändringen av stackstorleken, kan nu utföras.
resource.setrlimit(resource.RLIMIT_STACK, (-1, -1))
print(resource.getrlimit(resource.RLIMIT_STACK))
# (-1, -1)
recu_test(10 ** 5)
# Finish
Här är den mjuka gränsen satt till -1 (ingen gräns) för ett enkelt experiment, men i verkligheten skulle det vara säkrare att begränsa den till ett lämpligt värde.
När jag dessutom försökte ställa in en obegränsad soft limit även på min Mac uppstod följande fel.ValueError: not allowed to raise maximum limit
Att köra skriptet med sudo hjälpte inte. Det kan vara begränsat av systemet.
En process med en superanvändares effektiva UID kan begära vilken rimlig begränsning som helst, inklusive ingen begränsning.
En begäran som överskrider systemets gräns kommer dock fortfarande att resultera i ett ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation
Windows har ingen resursmodul, och mac kunde inte ändra den maximala stackstorleken på grund av systembegränsningar. Om vi kan öka stackstorleken på något sätt bör vi kunna lösa segmenteringsfelet, men vi har inte kunnat bekräfta detta.