Почему в Python есть ограничение на количество вложенных статических блоков?

Количество статически вложенных блоков в Python ограничено 20. То есть, вложение 19 циклов for будет в порядке (хотя и занимает слишком много времени; O(n^19) — это безумие), но вложение 20 циклов потерпит неудачу с:

SyntaxError: too many statically nested blocks

Какова основная причина наличия такого ограничения? Есть ли способ увеличить лимит?


person Right leg    schedule 07.07.2017    source источник
comment
Почему вы хотите увеличить это? Для вас это практический вопрос?   -  person Chris_Rands    schedule 07.07.2017
comment
Я все еще задаюсь вопросом об актуальности этого в реальной жизни. Я бы предположил, что достижение этого предела возможно только с безумным спагетти-кодом. Или что мне здесь не хватает?   -  person GhostCat    schedule 07.07.2017
comment
@GhostCat Два слова: автоматически сгенерированный код.   -  person Federico Poloni    schedule 08.07.2017
comment
@Jason Тот же предел должен применяться к вложенным if. 20 вложенных if являются менее надуманным примером, чем 20 вложенных for. Аргумент, занимающий слишком много времени, к ним не относится.   -  person Federico Poloni    schedule 08.07.2017


Ответы (3)


Это ограничение применяется не только к циклам for, но и ко всем другим блокам потока управления. Ограничение на количество вложенных блоков потока управления определяется внутри кода .h с константой CO_MAXBLOCKS:

#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */

Эта константа используется для установки максимального размера стека, который Python использует для выполнения исключений и циклов с именем blockstack. Это ограничение распространяется на все объекты фреймов и показано в frameobject.h :

int blockstack[CO_MAXBLOCKS];       /* Walking the 'finally' blocks */

Наиболее вероятной причиной этого ограничения является поддержание разумного уровня использования памяти при выполнении вложенных блоков. Вероятно, это похоже на предел Python требует рекурсивных вызовов. Это ограничение можно увидеть в compile.c. :

if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
    PyErr_SetString(PyExc_SyntaxError,
                    "too many statically nested blocks");
    return 0;
}

Более конкретный ответ о том, почему у Python есть это конкретное ограничение и почему они не могут от него избавиться, дал Майкл Хадсон в письме из списка рассылки Python 2004 г.:

Точно. Это связано с «стеком блоков», очень внутренней деталью реализации Python. Мы хотели бы избавиться от него (нет, потому что хотим, чтобы люди писали код с более чем 20 вложенными циклами for :-), но это не особенно легко (наконец: блоки — самая большая проблема) .

Обратите внимание, что в Python 2.6 и ниже нарушение максимального количества вложенных циклов привело бы к SystemError, а не SyntaxError. Однако это было изменено в Python 3 и исправлено обратно в Python 2.7, поэтому вместо этого будет поднято SyntaxError. Это было задокументировано в #issue 27514:

Проблема № 27514: слишком много статически вложенных блоков сделать SyntaxError вместо SystemError.

Причину такого изменения типов исключений указал Сергей Сторчака:

[...] SystemError не является исключением, которое следует вызывать. SystemError предназначена для ошибок, которые не могут возникнуть в обычном случае. Это должно быть вызвано только неправильным использованием C API или взломом внутренних компонентов Python. Я думаю, что SyntaxError больше подходит в этом случае [...].

person Christian Dean    schedule 07.07.2017
comment
Итак, решение (которое никто не рекомендует) для проблемы (которой на самом деле ни у кого нет) состоит в том, чтобы взломать исходный код CPython? - person Chris_Rands; 07.07.2017
comment
@Chris_Rands Да, наверное, да. Если вы измените CO_MAXBLOCKS, а затем перекомпилируете, я думаю, теоретически у вас может быть более двадцати вложенных блоков. - person Christian Dean; 07.07.2017
comment
@cᴏʟᴅsᴘᴇᴇᴅ Я этого не видел, но наборы тестов, скорее всего, тоже будут жаловаться github.com/python/cpython/blob/ и github.com/python/cpython/blob/ - person Chris_Rands; 07.07.2017
comment
@Chris_Rands Я верю, что вы можете сделать сборку без тестов, если я не ошибаюсь. - person cs95; 07.07.2017
comment
@Rightleg Спасибо. Мне было весело писать этот ответ. Честно говоря, я никогда не знал, что у Python есть такой предел. Я тоже узнал кое-что новое. - person Christian Dean; 07.07.2017

Это связано с blockstack, который является стек адресов байт-кода и используется для выполнения блоков кода, таких как циклы и исключения.

Так уж получилось, что в версии C (более ранней, чем C99) это ограничение было установлено на 20, а поскольку интерпретатор CPython построен на C, соблюдалось то же соглашение:

#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */

Константа 20, кажется, установлена ​​не по соглашению, и не более того.

[Ссылки предоставлены Кристианом Дином.]


Почему ограничение 20?

Если аргумент условности неубедителен, взгляните на The Zen of Python:

In [4]: import this
The Zen of Python, by Tim Peters

...
Flat is better than nested.
...

Как можно увеличить это значение?

Поскольку это значение является жестко запрограммированной константой, единственный способ изменить его для работы в ваших программах — это пересобрать дистрибутив Python и запустить скрипт в новой сборке.

  1. Загрузите исходный код cpython с github.

  2. Перейдите к cpython/Include/code.h

  3. Измените значение CO_MAXBLOCKS на любое большее, чем 20

  4. Перекомпилируйте Python (отключите тесты, они будут жаловаться)

person cs95    schedule 07.07.2017
comment
Вы знаете, C99 (единственный стандарт, который я только что нашел) допускает минимум 127 уровней вложенности. Это немного больше, чем 20. И GCC, например, использует все, что у нас есть ресурсы для компиляции в любом случае. - person Deduplicator; 08.07.2017
comment
@Deduplicator Да, я видел, что это предел для C99. Это скорее условность, чем какое-либо наложенное ограничение, кроме ограничения памяти. - person cs95; 08.07.2017
comment
На самом деле число 20 имеет для меня какой-то смысл, оно не совсем случайное: рекомендуемые настройки — 4 пробела для отступа и 80 символов в строке. И 80/4 = 20. - person Federico Poloni; 08.07.2017
comment
ANSI C также не устанавливает ограничение вложенности на 20. Это 15 для составных операторов, структур управления итерацией и структур управления выбором. Таким образом, уровень 20 должен был быть выбран для более позднего стандарта C с более высоким пределом, и он был (как вы уже сказали) выбран довольно произвольно. - person Cody Gray; 08.07.2017
comment
@CodyGray Спасибо, что разъяснили это. Я так и не нашел официального источника этой информации, только обсуждения на различных форумах. - person cs95; 08.07.2017

См. ответ здесь: слишком много статически вложенных блоков python Вы не можете увеличить его, поскольку он встроен в синтаксис Python. Ограничение применяется к любому типу стека кода (исключения, циклы и т. д.) и является решением разработчиков (предположительно, чтобы разумно использовать память). Одна странная вещь здесь: https://github.com/python/cpython/blob/6f0eb93183519024cb360162bdd81b9faec97ba6/Include/code.h#L95 там написано, что 20 — это максимальное число в функции. Но я только что попытался вложить 23 цикла for, а не внутри функции, и вы все равно получаете ошибку.

person Cary Shindell    schedule 07.07.2017
comment
Я полагаю, что в функции просто означает, что если вы вызываете другую функцию на уровне 20, эта функция все еще может вкладываться в 20 глубин. - person Carsten S; 08.07.2017
comment
О, это имеет смысл - person Cary Shindell; 08.07.2017