我刚刚意识到CPython似乎对待常量表达式,它们表示相同的值,而常量折叠方面却有所不同。例如:
>>> import dis
>>> dis.dis('2**66')
1 0 LOAD_CONST 0 (2)
2 LOAD_CONST 1 (66)
4 BINARY_POWER
6 RETURN_VALUE
>>> dis.dis('4**33')
1 0 LOAD_CONST 2 (73786976294838206464)
2 RETURN_VALUE
对于第二个示例,应用恒定折叠,而对于第一个示例,则不是两个折叠都代表相同的值。由于似乎也折叠了以下表达式,所以它似乎与指数的值无关,也与结果的大小无关。
>>> dis.dis('2.0**66')
1 0 LOAD_CONST 2 (7.378697629483821e+19)
2 RETURN_VALUE
>>> dis.dis('4**42')
1 0 LOAD_CONST 2 (19342813113834066795298816)
2 RETURN_VALUE
为什么前两个表达式会被不同地对待,并且更普遍地讲,CPython进行恒定折叠时遵循哪些具体规则?
经过测试:
$ python3.6 --version
Python 3.6.5 :: Anaconda, Inc.
$ python3.7 --version
Python 3.7.1
答案 0 :(得分:3)
没有规则,可以不断折叠。只有实现细节。它们以前已经更改,并且将再次更改。
哎呀,您甚至不能谈论“ Python 3行为”或“ Python 3.6行为”,因为这些实现细节在3.6。 4 和3.6。 5之间进行了更改。 。在3.6.4上,2**66
示例被固定折叠。
就目前而言,没有人知道“ for now”将持续多久,实现细节是AST优化器包括一些安全措施,以防止在固定折叠上花费过多的时间或内存。 2**66
或4**33
的{{3}}基于LHS中的位数和RHS的值:
if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w) > 0) {
size_t vbits = _PyLong_NumBits(v);
size_t wbits = PyLong_AsSize_t(w);
if (vbits == (size_t)-1 || wbits == (size_t)-1) {
return NULL;
}
if (vbits > MAX_INT_SIZE / wbits) {
return NULL;
}
}
MAX_INT_SIZE
早于#define
,早于128。由于2
是2位数字,而4
是3位数字,因此估计结果的大小较小4**33
,因此它通过了支票并被固定折叠。
在Python 3.6.5上,实现细节基本相似,但是这种不断的折叠发生在safeguard中,而不是在3.6.5中不存在的AST优化器中。
在Python 3.6.4上,预检查保护措施不存在。窥孔优化器在计算出bytecode peephole optimizer的恒定折叠结果后,它们会产生太大的恒定折叠结果,从而导致阈值与预先检查不同。