У 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.
- Unix Specific Services — Python 3.10.0 Documentation
- resource — Resource usage information — Python 3.10.0 Documentation
З дапамогай resource.getrlimit () вы можаце атрымаць ліміт рэсурсу, паказаны ў аргуменце, у выглядзе корціка (мяккі ліміт, жорсткі ліміт). Тут мы паказваем 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 КБ = 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 не змог змяніць максімальны памер стэка з -за абмежаванняў сістэмы. Калі мы можам нейкімі спосабамі павялічыць памер стэка, мы павінны быць у стане ліквідаваць памылку сегментацыі, але мы не змаглі гэта пацвердзіць.