Праверыць і змяніць мяжу рэкурсіі 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) будзе быць паднятым.

再 帰 限界 は 、 再 帰 の 限界 は な く tho 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 не змог змяніць максімальны памер стэка з -за абмежаванняў сістэмы. Калі мы можам нейкімі спосабамі павялічыць памер стэка, мы павінны быць у стане ліквідаваць памылку сегментацыі, але мы не змаглі гэта пацвердзіць.