Почему предел максимальной глубины рекурсии в Python 2 ** 32/2 - 31?

В Python некоторые программы создают ошибку RecursionError: maximum recursion depth exceeded in comparison или что-то подобное.

Это связано с тем, что существует ограничение на глубину вложенности рекурсии.

Чтобы получить текущее значение максимальной глубины рекурсии, используйте

import sys
sys.getrecursionlimit()

Значение по умолчанию составляет 1000 или 1500 в зависимости от конкретной комбинации system/python/....

Значение можно увеличить, чтобы получить доступ к более глубокой рекурсии.

import sys
sys.setrecursionlimit(LIMIT)

где LIMIT — новый предел.

Это ошибка, которую я получаю, когда превышаю предел «предела»:

OverflowError: Python int too large to convert to C int

Почему «лимит» лимит int(2**32 / 2) - 31?

Я ожидаю, что это может быть int(2**32 / 2) - 1, поскольку целые числа являются знаковыми (32-битными) числами, но почему - 31?

Впервые этот вопрос возник, когда я тестировал версию 3.9.13 (64-разрядную версию). Я также тестировал версию 3.12 (64-разрядную версию).

Обновлено: Вот демо-версия IDLE с Python 3.9.13.

Python 3.9.13 (tags/v3.9.13:6de2ca5, May 17 2022, 16:36:42) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
>>> import sys
>>> v = 2**31-1
>>> print(v)
2147483647
>>> sys.setrecursionlimit(v)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    sys.setrecursionlimit(v)
OverflowError: Python int too large to convert to C int
>>> v = 2**31-30
>>> sys.setrecursionlimit(v)
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    sys.setrecursionlimit(v)
OverflowError: Python int too large to convert to C int
>>> v = 2**31-31
>>> sys.setrecursionlimit(v)
>>> 

вас может заинтересовать github.com/python/cpython/issues/112282

jhole 19.07.2024 17:29

Это (2**32)/2 - 1, то есть 2**31 - 1, то есть максимальное значение для целого числа C со знаком. Не знаю, откуда у вас -31. Вы хотите сказать, что Python в вашей системе будет жаловаться, если вы установите предел выше 2**31 - 31? У меня нет: i.sstatic.net/keSR2Ub8.png

Marco Bonelli 19.07.2024 17:31

Вы читали документацию? «Максимально возможный предел зависит от платформы».

wjandrea 19.07.2024 17:38

Не говоря уже о том, что int(x / y) эквивалентно x // y для положительных чисел (или, по крайней мере, положительных целых чисел в этом диапазоне).

wjandrea 19.07.2024 17:45

С другой стороны: из-за накладных расходов на вызов функций Python и того, как среда выполнения распределяет использование памяти, имейте в виду, что ваш истинный предел рекурсии будет намного ниже этого. Возможно, вы сможете достичь 100_000 без сбоя во время выполнения, но я бы не стал на это делать ставку.

jsbueno 19.07.2024 18:25

(хорошо - в более новой версии Python я без проблем заставил его работать с 10_миллионами рекурсивных вызовов. 100_миллион дал мне OOM - со старым (до 3.12) около 10_000 приводило к сбою среды выполнения)

jsbueno 19.07.2024 19:03

@jsbueno Да, чистая рекурсия Python теперь ограничена объемом вашей памяти, а не размером вашего стека C. Вы можете совершить более 10_000_000 вызовов на ГБ, если они экономны: Попробуйте это онлайн!

no comment 19.07.2024 20:25

Я использовал этот ответ для своего теста. До сих пор я запускал лимит-тест только на 3.12. Сейчас я еще и на нем протестировал функцию (ackermann) и вроде обе версии тоже ведут себя по-разному. Кажется, что в 3.9.13 используются все ~2 миллиарда и на самом деле заканчивается довольно быстро (без решения). На 3.12 работает дольше, чем хотелось бы дождаться короткого теста. Возможно, имелось в виду что-то вроде @jhole, но я НЕ использую LRU-кэш. В обеих версиях используется незначительное количество оперативной памяти.

Twistios_Player 19.07.2024 22:30
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
8
97
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Обычно предельным «пределом» будет 2**31 - 1, то есть максимальное значение для целого числа C со знаком (хотя максимально возможный предел документирован как зависящий от платформы).

Более низкий полезный диапазон, который вы получаете в IDLE, объясняется тем, что он оборачивает встроенную функцию для наложения нижнего предела:

>>> import sys
>>> sys.setrecursionlimit
<function setrecursionlimit at 0xcafef00d>
>>> sys.setrecursionlimit.__wrapped__
<built-in function setrecursionlimit>

Это специфично для IDLE, и в противном случае вы должны увидеть «полную» 2**31-1 глубину при использовании того же интерпретатора на той же платформе.

Вы можете найти обезьяний патч здесь, в исходниках CPython: Lib/idlelib/run.py:install_recursionlimit_wrappers

Используемые значения уменьшаются на RECURSIONLIMIT_DELTA=30 , а комментарий к ссылкам на функции-оболочки bpo-26806 (IDLE не отображает трассировки и зависания RecursionError), где Терри Дж. Риди упоминает причину исправления нижнего предела:

IDLE добавляет собственные вызовы в стек вызовов до (и, возможно, после) запуска пользовательского кода.

Вы также должны увидеть это в строке документации:

idle docstring

Здесь остался без ответа один вопрос: почему вообще требуется C int. Python реализован на языке C, так что, возможно, это неудивительно.

Mark Ransom 19.07.2024 19:39
help(sys.setrecursionlimit) тоже подойдет, правда?
no comment 19.07.2024 20:11

@nocomment, верно.

wim 19.07.2024 20:34

Из любопытства я проверил, что здесь делает PyPy, поскольку у них нет реальной причины использовать C int. На PyPy вас молча ограничивают до 1 миллиона! github.com/pypy/pypy/blob/…

wim 19.07.2024 20:49

Другой вопрос, почему он не использует беззнаковый вместо знаковый.

Twistios_Player 19.07.2024 22:02

Тем более, что разрешены только числа >= 1.

Twistios_Player 19.07.2024 22:35

Другие вопросы по теме

Похожие вопросы

Как рассчитать индекс относительной силы (RSI) с помощью итераций записи в фрейме данных pandas
Как устранить ошибку «Необходимо указать, как согласовать расходящиеся ветки» при перебазировании в Git?
Как сгруппировать строки на основе идентификатора столбца в фрейме данных pandas?
Интерпретация битовых данных в Python с помощью структур и ctypes: невыровненные результаты
Мой чат-сервер Python не отправляет сообщения должным образом
Как сообщить средству проверки типов тип вывода внешней функции
Разделить фрейм данных pandas на основе заданной строки строки
Как объединить подсказку типа с использованием переменной связанного типа и статических типов для максимальной гибкости?
Сравните строки из очень большого текстового файла (более 100 ГБ) с небольшим текстовым файлом (около 30 строк) и распечатайте все строки, содержащиеся в обоих файлах
Переместить часть строки в фрейме данных в новую строку