Comprobe e cambie o límite de recursión de Python (por exemplo, sys.setrecursionlimit)

Negocios

En Python, hai un límite superior para o número de recursións (o número máximo de recursións). Para executar unha función recursiva cun gran número de chamadas, é necesario cambiar o límite. Use as funcións do módulo sys da biblioteca estándar.

O número de recursións tamén está limitado polo tamaño da pila. Nalgúns ambientes, o módulo de recursos da biblioteca estándar pódese usar para cambiar o tamaño máximo da pila (funcionou en Ubuntu, pero non en Windows ou Mac).

A seguinte información ofrécese aquí.

  • Obtén o límite superior do número actual de recursións:sys.getrecursionlimit()
  • Cambia o límite superior do número de recursións:sys.setrecursionlimit()
  • Cambia o tamaño máximo da pila:resource.setrlimit()

O código de mostra está a executarse en Ubuntu.

Obter o límite de recursión actual: sys.getrecursionlimit ()

O límite de recursión actual pódese obter con sys.getrecursionlimit ().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

No exemplo, o número máximo de recursións é de 1000, que poden variar dependendo do seu contorno. Teña en conta que o recurso que importamos aquí usarase máis tarde, pero non en Windows.

Como exemplo, usaremos a seguinte función recursiva sinxela. Se se especifica un enteiro positivo n como argumento, o número de chamadas será n veces.

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

Aparecerá un erro (RecursionError) se tenta realizar unha recursión superior ao límite superior.

recu_test(950)
# Finish

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

Teña en conta que o valor obtido por sys.getrecursionlimit () non é estritamente o número máximo de recursións, senón a profundidade máxima da pila do intérprete de Python, polo que aínda que o número de recursións sexa lixeiramente inferior a este valor, producirase un erro (RecursionError) ser criado.

再 帰 限界 は 、 再 帰 の 限界 で は な く 、 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 o límite de recursión: sys.setrecursionlimit ()

O límite superior do número de recursións pódese cambiar con sys.setrecursionlimit (). O límite superior especifícase como argumento.

Permite realizar recursións máis profundas.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Se o límite superior especificado é demasiado pequeno ou demasiado grande, producirase un erro. Esta restrición (límites superior e inferior do propio límite) varía segundo o ambiente.

O valor máximo do límite depende da plataforma. Se precisa unha recursión profunda, pode especificar un valor maior dentro do rango admitido pola plataforma, pero ten en conta que este valor causará un fallo se é 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

O número máximo de recursións tamén está limitado polo tamaño da pila, como se explica a continuación.

Cambia o tamaño máximo da pila: resource.setrlimit ()

Mesmo se se establece un valor grande en sys.setrecursionlimit (), pode que non se execute se o número de recursións é grande. Prodúcese un fallo de segmentación do seguinte xeito.

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

# recu_test(10 ** 5)
# Segmentation fault

En Python, o módulo de recursos da biblioteca estándar pode usarse para cambiar o tamaño máximo da pila. Non obstante, o módulo de recursos é un módulo específico de Unix e non se pode usar en Windows.

Con resource.getrlimit (), pode obter o límite do recurso especificado no argumento como tupla de (límite brando, límite duro). Aquí, especificamos resource.RLIMIT_STACK como o recurso, que representa o tamaño máximo da pila de chamadas do proceso actual.

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

No exemplo, o límite suave é 8388608 (8388608 B = 8192 KB = 8 MB) e o límite duro é -1 (ilimitado).

Podes cambiar o límite do recurso con resource.setrlimit (). Aquí, o límite suave tamén se establece en -1 (sen límite). Tamén pode usar o recurso constante.RLIM_INFINIT para representar o límite ilimitado.

Agora pódese realizar unha recursión profunda, que non se puido realizar debido a un fallo de segmentación antes de cambiar o tamaño da pila.

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

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

recu_test(10 ** 5)
# Finish

Aquí, o límite suave establécese en -1 (sen límite) para un experimento sinxelo, pero en realidade, sería máis seguro limitalo a un valor adecuado.

Ademais, cando tentei establecer un límite de software ilimitado tamén no meu Mac, produciuse o seguinte erro.ValueError: not allowed to raise maximum limit
Executar o script con sudo non axudou. Pode estar restrinxido polo sistema.

Un proceso co UID efectivo dun superusuario pode solicitar calquera límite razoable, incluído ningún límite.
Non obstante, unha solicitude que supere o límite imposto polo sistema aínda producirá un ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

Windows non ten un módulo de recursos e mac non puido cambiar o tamaño máximo da pila debido ás limitacións do sistema. Se podemos aumentar o tamaño da pila dalgún medio, deberíamos ser capaces de resolver o fallo de segmentación, pero non puidemos confirmalo.