Comprobar y cambiar el límite de recursión de Python (por ejemplo, sys.setrecursionlimit)

Negocio

En Python, hay un límite superior al número de recursiones (el número máximo de recursiones). Para ejecutar una función recursiva con un gran número de llamadas, es necesario cambiar el límite. Utilice las funciones del módulo sys de la biblioteca estándar.

El número de recursiones también está limitado por el tamaño de la pila. En algunos entornos, el módulo de recursos de la biblioteca estándar se puede utilizar para cambiar el tamaño máximo de la pila (funcionó en Ubuntu, pero no en Windows o mac).

La siguiente información se proporciona aquí.

  • Obtiene el límite superior del número actual de recurrencias:sys.getrecursionlimit()
  • Cambiar el límite superior del número de recurrencias:sys.setrecursionlimit()
  • Cambia el tamaño máximo de la pila:resource.setrlimit()

El código de ejemplo se ejecuta en Ubuntu.

Obtener el límite de recursión actual: sys.getrecursionlimit()

El límite de recursión actual se puede obtener con sys.getrecursionlimit().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

En el ejemplo, el número máximo de recurrencias es de 1000, que puede variar dependiendo de su entorno. Tenga en cuenta que el recurso que estamos importando aquí se utilizará más adelante, pero no en Windows.

Como ejemplo, utilizaremos la siguiente función recursiva simple. Si se especifica un número entero positivo n como argumento, el número de llamadas será n veces.

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

Se producirá un error (RecursionError) si se intenta realizar una recursión más allá del límite superior.

recu_test(950)
# Finish

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

Tenga en cuenta que el valor obtenido por sys.getrecursionlimit() no es estrictamente el número máximo de recurrencias, sino la profundidad máxima de la pila del intérprete de Python, por lo que incluso si el número de recurrencias es ligeramente inferior a este valor, se producirá un error (RecursionError).

El límite de recursión no es el límite de recursión, sino la profundidad máxima de la pila del intérprete de 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

Cambiar el límite de recursión: sys.setrecursionlimit()

El límite superior del número de recursiones puede cambiarse mediante sys.setrecursionlimit(). El límite superior se especifica como argumento.

Permite realizar una recursión más profunda.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Si el límite superior especificado es demasiado pequeño o demasiado grande, se producirá un error. Esta restricción (límites superior e inferior del propio límite) varía en función del entorno.

El valor máximo del límite depende de la plataforma. Si necesitas una recursión profunda, puedes especificar un valor mayor dentro del rango soportado por la plataforma, pero ten en cuenta que este valor provocará un fallo si es demasiado 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

El número máximo de recurrencias también está limitado por el tamaño de la pila, como se explica a continuación.

Cambiar el tamaño máximo de la pila: resource.setrlimit()

Aunque se establezca un valor grande en sys.setrecursionlimit(), puede que no se ejecute si el número de recurrencias es grande. Un fallo de segmentación ocurre de la siguiente manera.

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

# recu_test(10 ** 5)
# Segmentation fault

En Python, el módulo de recursos de la biblioteca estándar se puede utilizar para cambiar el tamaño máximo de la pila. Sin embargo, el módulo de recursos es un módulo específico de Unix y no se puede utilizar en Windows.

Con resource.getrlimit(), puedes obtener el límite del recurso especificado en el argumento como una tupla de (límite suave, límite duro). Aquí, especificamos resource.RLIMIT_STACK como el recurso, que representa el tamaño máximo de la pila de llamadas del proceso actual.

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

En el ejemplo, el límite suave es 8388608 (8388608 B = 8192 KB = 8 MB) y el límite duro es -1 (ilimitado).

Puedes cambiar el límite del recurso con resource.setrlimit(). Aquí, el límite suave también se establece en -1 (sin límite). También puede utilizar la constante resource.RLIM_INFINIT para representar el límite ilimitado.

La recursión profunda, que no podía realizarse debido a un fallo de segmentación antes del cambio de tamaño de la pila, ahora puede realizarse.

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

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

recu_test(10 ** 5)
# Finish

Aquí, el límite suave se establece en -1 (sin límite) para un experimento simple, pero en realidad, sería más seguro limitarlo a un valor apropiado.

Además, cuando intenté establecer un límite suave ilimitado también en mi mac, se produjo el siguiente error.ValueError: not allowed to raise maximum limit
Ejecutar el script con sudo no ayudó. Puede que esté restringido por el sistema.

Un proceso con el UID efectivo de un superusuario puede solicitar cualquier límite razonable, incluso ninguno.
Sin embargo, una solicitud que supere el límite impuesto por el sistema seguirá dando lugar a un ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

Windows no tiene un módulo de recursos, y mac no pudo cambiar el tamaño máximo de la pila debido a las limitaciones del sistema. Si podemos aumentar el tamaño de la pila por algún medio, deberíamos poder resolver el fallo de segmentación, pero no hemos podido confirmarlo.