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

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

SyntaxError: too many statically nested blocks

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

3 ответа

Решение

Этот предел распространяется не только на for петли, но ко всем другим блокам управления потоком, а также. Ограничение на количество вложенных блоков потока управления определяется внутри code.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 года:

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

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

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

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

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

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

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

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

Постоянная 20 Похоже, что это не входит в соглашение, и не более того.

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


Почему предел 20?

Если аргумент соглашения не убедителен, взгляните на Дзен Питона:

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 (отключите тесты, они будут жаловаться)

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

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