В 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.
- Unix Specific Services — Python 3.10.0 Documentation
- resource — Resource usage information — Python 3.10.0 Documentation
С помощью resource.getrlimit() можно получить предел ресурса, указанного в аргументе, в виде кортежа (soft limit, hard limit). Здесь мы указываем resource.RLIMIT_STACK в качестве ресурса, который представляет собой максимальный размер стека вызовов текущего процесса.
- 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)
В примере мягкий лимит равен 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 не смог изменить максимальный размер стека из-за ограничений системы. Если мы сможем увеличить размер стека каким-либо способом, то нам удастся решить проблему ошибки сегментации, но мы не смогли это подтвердить.