为什么Python对可以嵌套的静态块的数量有限制?

Rig*_*leg 59 python language-implementation nested-loops

Python中静态嵌套块的数量限制为20.也就是说,嵌套19个for循环将很好(虽然过于耗时; O(n^19)是疯狂的),但嵌套20将失败:

SyntaxError: too many statically nested blocks
Run Code Online (Sandbox Code Playgroud)

有这样限制的根本原因是什么?有没有办法增加限额?

Chr*_*ean 75

此限制不仅适用于for循环,也适用于所有其他控制流程块.嵌套控制流块数量的限制在code.h中定义,并带有一个名为的常量CO_MAXBLOCKS:

#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */
Run Code Online (Sandbox Code Playgroud)

此常量用于设置Python用于执行命名的异常和循环的堆栈的最大大小blockstack.此限制强加于所有框架对象,并显示在frameobject.h中:

int blockstack[CO_MAXBLOCKS];       /* Walking the 'finally' blocks */
Run Code Online (Sandbox Code Playgroud)

此限制的最可能原因是在执行嵌套块时将内存使用保持在理智水平.它可能类似于Python对递归调用的限制.可以看到在compile.c中强制执行此限制:

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

Michael Hudson在2004年的Python邮件列表信函中给出了一个更为具体的答案:为什么Python有这个特定的限制以及为什么他们无法摆脱它?

发现.这与'blockstack'有关,这是Python实现的内部细节.我们想要摆脱它(不是 因为我们想让人们编写带有超过20个嵌套for循环的代码:-)但它并不是特别容易(最后:块是最大的问题).

请注意,在Python 2.6及更低版本中,打破嵌套循环的最大数量会导致SystemError不是a SyntaxError.然而,这在Python 3中已经改变,并且已经回调到Python 2.7,因此SyntaxError会引发一个.这在#issue 27514中记录:

问题#27514:使太多静态嵌套的块成为SyntaxError而不是SystemError.

Serhiy Storchaka给出了异常类型发生这种变化的原因:

[...] SystemError不是应该引发的异常.SystemError用于在正常情况下无法发生的错误.它应该只是由于错误地使用C API或黑客攻击Python内部造成的.我认为在这种情况下,SyntaxError更合适[...].

  • @Rightleg谢谢.我很开心写这个答案.说实话,我从来不知道Python有这个限制.我也学到了一些新知识. (6认同)
  • 因此问题(没有人实际拥有)的解决方案(没有人推荐)是破解CPython源代码? (2认同)
  • @Chris_Rands 是的,我想是的。如果你改变`CO_MAXBLOCKS`然后重新编译,我猜理论上你可能有超过二十个嵌套块。 (2认同)

cs9*_*s95 22

这与blockstack字节码地址的堆栈有关,用于执行循环和异常等代码块.

碰巧的是,C版本(早于C99)已经设置了此限制20,并且由于CPython解释器是使用C构建的,因此遵循了相同的约定:

#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */
Run Code Online (Sandbox Code Playgroud)

这个常数20似乎是出于惯例,仅此而已.

[链接由Christian Dean提供.]


为什么限制20?

如果约定的论证不具说服力,那么看看Python的禅宗:

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

...
Flat is better than nested.
...
Run Code Online (Sandbox Code Playgroud)

你怎么能增加这个价值?

由于此值是一个硬编码常量,因此在程序中更改它的唯一方法是重建python发行版并在新版本上运行脚本.

  1. github下载cpython源代码

  2. 导航 cpython/Include/code.h

  3. 将值更改为CO_MAXBLOCKS大于20 的值

  4. 重新编译Python(禁用测试,他们会抱怨)

  • 实际上数字20对我来说是有意义的,它并不是完全随机的:建议的设置是缩进的4个空格,每行80个字符.并且80/4 = 20. (9认同)
  • 你知道,C99(我刚刚发现的一个标准)允许至少127级嵌套.这有点超过20个.而GCC就是为了"无论我们有什么资源可以编译". (5认同)