Controllare e cambiare il limite di ricorsione di Python (ad esempio sys.setrecursionlimit)

Attività commerciale

In Python, c'è un limite superiore al numero di ricorsi (il numero massimo di ricorsi). Per eseguire una funzione ricorsiva con un gran numero di chiamate, è necessario cambiare il limite. Usate le funzioni del modulo sys della libreria standard.

Il numero di ricorsi è anche limitato dalla dimensione dello stack. In alcuni ambienti, il modulo risorse della libreria standard può essere usato per cambiare la dimensione massima dello stack (ha funzionato su Ubuntu, ma non su Windows o mac).

Le seguenti informazioni sono fornite qui.

  • Ottiene il limite superiore del numero corrente di ricorsi:sys.getrecursionlimit()
  • Cambia il limite superiore del numero di ricorsi:sys.setrecursionlimit()
  • Cambia la dimensione massima dello stack:resource.setrlimit()

Il codice di esempio è in esecuzione su Ubuntu.

Ottenere il limite di ricorsione corrente: sys.getrecursionlimit()

Il limite di ricorsione corrente può essere ottenuto con sys.getrecursionlimit().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

Nell'esempio, il numero massimo di ricorsi è 1000, che può variare a seconda del vostro ambiente. Nota che la risorsa che stiamo importando qui sarà usata più tardi, ma non su Windows.

Come esempio, useremo la seguente semplice funzione ricorsiva. Se un intero positivo n è specificato come argomento, il numero di chiamate sarà n volte.

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

Un errore (RecursionError) verrà sollevato se si cerca di eseguire la ricorsione più del limite superiore.

recu_test(950)
# Finish

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

Si noti che il valore ottenuto da sys.getrecursionlimit() non è strettamente il numero massimo di ricorsi, ma la profondità massima dello stack dell'interprete Python, quindi anche se il numero di ricorsi è leggermente inferiore a questo valore, verrà sollevato un errore (RecursionError).

Il limite di ricorsione non è il limite della ricorsione, ma la profondità massima dello stack dell'interprete python.
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

Cambia il limite di ricorsione: sys.setrecursionlimit()

Il limite superiore del numero di ricorsi può essere cambiato da sys.setrecursionlimit(). Il limite superiore è specificato come argomento.

Permette di eseguire una ricorsione più profonda.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Se il limite superiore specificato è troppo piccolo o troppo grande, si verifica un errore. Questo vincolo (limiti superiore e inferiore del limite stesso) varia a seconda dell'ambiente.

Il valore massimo del limite dipende dalla piattaforma. Se avete bisogno di una ricorsione profonda, potete specificare un valore più grande entro l'intervallo supportato dalla piattaforma, ma siate consapevoli che questo valore causerà un crash se è troppo grande.
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

Il numero massimo di ricorsi è anche limitato dalla dimensione dello stack, come spiegato in seguito.

Cambia la dimensione massima dello stack: resource.setrlimit()

Anche se un grande valore è impostato in sys.setrecursionlimit(), potrebbe non essere eseguito se il numero di ricorsi è grande. Un errore di segmentazione si verifica come segue.

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

# recu_test(10 ** 5)
# Segmentation fault

In Python, il modulo delle risorse nella libreria standard può essere usato per cambiare la dimensione massima dello stack. Tuttavia, il modulo delle risorse è un modulo specifico di Unix e non può essere usato su Windows.

Con resource.getrlimit(), potete ottenere il limite della risorsa specificata nell'argomento come una tupla di (soft limit, hard limit). Qui, specifichiamo resource.RLIMIT_STACK come risorsa, che rappresenta la dimensione massima dello stack delle chiamate del processo corrente.

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

Nell'esempio, il limite morbido è 8388608 (8388608 B = 8192 KB = 8 MB) e il limite duro è -1 (illimitato).

Puoi cambiare il limite della risorsa con resource.setrlimit(). Qui, anche il limite soft è impostato a -1 (nessun limite). Puoi anche usare la costante resource.RLIM_INFINIT per rappresentare il limite illimitato.

La ricorsione profonda, che non poteva essere eseguita a causa di un errore di segmentazione prima della modifica della dimensione dello stack, può ora essere eseguita.

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

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

recu_test(10 ** 5)
# Finish

Qui, il limite morbido è impostato a -1 (nessun limite) per un semplice esperimento, ma in realtà, sarebbe più sicuro limitarlo a un valore appropriato.

Inoltre, quando ho provato a impostare un limite soft illimitato anche sul mio mac, si è verificato il seguente errore.ValueError: not allowed to raise maximum limit
Eseguire lo script con sudo non ha aiutato. Potrebbe essere limitato dal sistema.

Un processo con l'UID effettivo di un superutente può richiedere qualsiasi limite ragionevole, incluso nessun limite.
Tuttavia, una richiesta che supera il limite imposto dal sistema risulterà comunque in un ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

Windows non ha un modulo risorse, e mac non può cambiare la dimensione massima dello stack a causa delle limitazioni del sistema. Se possiamo aumentare la dimensione dello stack con qualche mezzo, dovremmo essere in grado di risolvere l'errore di segmentazione, ma non siamo stati in grado di confermarlo.