Перевірте та змініть ліміт рекурсії 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

Якщо вказана верхня межа занадто мала або занадто велика, виникне помилка. Це обмеження (верхня та нижня межі самої межі) змінюється залежно від середовища.

Максимальне значення ліміту залежить від платформи. Якщо вам потрібна глибока рекурсія, ви можете вказати більше значення в діапазоні, який підтримує платформа, але майте на увазі, що це значення спричинить збій, якщо воно буде занадто великим.
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 модуль ресурсів у стандартній бібліотеці можна використовувати для зміни максимального розміру стека. Однак модуль ресурсів є спеціальним модулем Unix і не може використовуватися в Windows.

За допомогою resource.getrlimit() ви можете отримати обмеження ресурсу, зазначеного в аргументі, як кортеж (м’яка межа, жорстке обмеження). Тут ми вказуємо resource.RLIMIT_STACK як ресурс, який представляє максимальний розмір стека викликів поточного процесу.

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

У прикладі м’яке обмеження становить 8388608 (8388608 B = 8192 КБ = 8 МБ), а жорстке – -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 не може змінити максимальний розмір стека через системні обмеження. Якщо ми зможемо якимось чином збільшити розмір стека, ми зможемо вирішити помилку сегментації, але ми не змогли це підтвердити.