Проверка и изменение предела рекурсии Python (например, sys.setrecursionlimit)

Бизнес

В Python существует верхний предел количества рекурсий (максимальное количество рекурсий). Чтобы выполнить рекурсивную функцию с большим количеством вызовов, необходимо изменить этот предел. Используйте функции в модуле sys стандартной библиотеки.

Количество рекурсий также ограничено размером стека. В некоторых средах для изменения максимального размера стека можно использовать модуль ресурсов стандартной библиотеки (это работает на Ubuntu, но не на Windows или mac).

Здесь представлена следующая информация.

  • Получение верхней границы текущего числа рекурсий:sys.getrecursionlimit()
  • Изменение верхнего предела количества рекурсий:sys.setrecursionlimit()
  • Изменение максимального размера стека:resource.setrlimit()

Код примера работает на Ubuntu.

Получите текущий предел рекурсии: sys.getrecursionlimit().

Текущий предел рекурсии можно получить с помощью sys.getrecursionlimit().

import sys
import resource

print(sys.getrecursionlimit())
# 1000

В примере максимальное число рекурсий равно 1000, что может варьироваться в зависимости от вашей среды. Обратите внимание, что ресурс, который мы импортируем здесь, будет использоваться позже, но не в Windows.

В качестве примера мы будем использовать следующую простую рекурсивную функцию. Если в качестве аргумента указано целое положительное число n, то количество вызовов будет равно n раз.

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

Ошибка (RecursionError) будет выдана, если вы попытаетесь выполнить рекурсию больше верхнего предела.

recu_test(950)
# Finish

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

Обратите внимание, что значение, полученное sys.getrecursionlimit(), не является строго максимальным количеством рекурсий, а максимальной глубиной стека интерпретатора Python, поэтому даже если количество рекурсий будет немного меньше этого значения, будет выдана ошибка (RecursionError).

Предел рекурсии — это не предел рекурсии, а максимальная глубина стека интерпретатора 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

Изменение предела рекурсии: sys.setrecursionlimit().

Верхний предел количества рекурсий можно изменить с помощью sys.setrecursionlimit(). Верхний предел указывается в качестве аргумента.

Позволяет выполнять более глубокую рекурсию.

sys.setrecursionlimit(2000)

print(sys.getrecursionlimit())
# 2000

recu_test(1500)
# Finish

Если указанный верхний предел слишком мал или слишком велик, возникнет ошибка. Это ограничение (верхняя и нижняя границы самого ограничения) варьируется в зависимости от среды.

Максимальное значение limit зависит от платформы. Если вам нужна глубокая рекурсия, вы можете указать большее значение в диапазоне, поддерживаемом платформой, но имейте в виду, что слишком большое значение приведет к сбою.
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

Максимальное количество рекурсий также ограничено размером стека, как объясняется далее.

Изменение максимального размера стека: resource.setrlimit()

Даже если в sys.setrecursionlimit() задано большое значение, она может не выполниться, если количество рекурсий велико. Ошибка сегментации происходит следующим образом.

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

# recu_test(10 ** 5)
# Segmentation fault

В Python для изменения максимального размера стека можно использовать модуль resource в стандартной библиотеке. Однако модуль ресурсов является специфичным для Unix и не может быть использован в Windows.

С помощью resource.getrlimit() можно получить предел ресурса, указанного в аргументе, в виде кортежа (soft limit, hard limit). Здесь мы указываем resource.RLIMIT_STACK в качестве ресурса, который представляет собой максимальный размер стека вызовов текущего процесса.

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

В примере мягкий лимит равен 8388608 (8388608 B = 8192 KB = 8 MB), а жесткий — -1 (неограниченно).

Вы можете изменить лимит ресурса с помощью resource.setrlimit(). Здесь мягкий лимит также установлен на -1 (нет лимита). Вы также можете использовать константу resource.RLIM_INFINIT для обозначения неограниченного лимита.

Глубокая рекурсия, которая не могла быть выполнена из-за ошибки сегментации до изменения размера стека, теперь может быть выполнена.

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

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

recu_test(10 ** 5)
# Finish

Здесь мягкий предел установлен на -1 (нет предела) для простого эксперимента, но в реальности было бы безопаснее ограничить его соответствующим значением.

Кроме того, когда я попытался установить неограниченный мягкий лимит и на своем mac, возникла следующая ошибка.ValueError: not allowed to raise maximum limit
Запуск скрипта с помощью sudo не помог. Возможно, он ограничен системой.

Процесс с эффективным UID суперпользователя может запросить любое разумное ограничение, включая отсутствие ограничения.
Однако запрос, превышающий установленный системой лимит, все равно приведет к ошибке ValueError.
resource.setrlimit() — Resource usage information — Python 3.10.0 Documentation

В Windows нет модуля ресурсов, а mac не смог изменить максимальный размер стека из-за ограничений системы. Если мы сможем увеличить размер стека каким-либо способом, то нам удастся решить проблему ошибки сегментации, но мы не смогли это подтвердить.

Copied title and URL