Python 3.6.5 "is"和"=="超出缓存间隔的整数



我想在前面说,我知道==is之间的区别,一个用于引用,另一个用于对象。我还知道python在启动时会缓存范围为(-5, 256)的整数,所以在将它们与is进行比较时,它们应该可以工作。

然而,我看到了一种奇怪的行为。

>>> 2**7 is 2**7
True
>>> 2**10 is 2**10
False

这是意料之中的,2**71282**101024,一个在区间(-5, 256),另一个不在。

然而。。。

>>> 10000000000000000000000000000000000000000 is 10000000000000000000000000000000000000000
True

为什么返回True?它显然是高于任何类型的缓存间隔的值WAY,并且2**10 is 2**10清楚地表明is实际上不适用于高于256的整数。那么…为什么会发生这种情况?

CPython检测代码中的常量值,并重新使用它们来节省内存。这些常量存储在代码对象上,甚至可以从python中访问:

>>> codeobj = compile('999 is 999', '<stdin>', 'exec')
>>> codeobj
<code object <module> at 0x7fec489ef420, file "<stdin>", line 1>
>>> codeobj.co_consts
(999, None)

is的两个操作数都指向这个相同的999整数。我们可以通过分解代码来确认这一点:

>>> import dis
>>> dis.dis(codeobj)
1           0 LOAD_CONST               0 (999)
2 LOAD_CONST               0 (999)
4 COMPARE_OP               8 (is)
6 POP_TOP
8 LOAD_CONST               1 (None)
10 RETURN_VALUE

正如您所看到的,前两条LOAD_CONST指令都加载了索引为0的常量,即999数字。

但是,只有当两个数字同时编译时才会发生这种情况。如果你在一个单独的代码对象中创建每个数字,它们将不再相同:

>>> x = 999
>>> x is 999
False

请记住Python是编译的。表达式是一次性编译的,并且在可能的情况下共享其文字。任何运算,如求幂,或从一侧加减1,都会破坏恒等式。(理论上,Python可以进行常数折叠,从而扩展is相同的表达式集,但这并不麻烦。(

执行多次编译也将打破这一点:

>>> x=300
>>> x is 300
False

相关内容

  • 没有找到相关文章

最新更新