我想在前面说,我知道==
和is
之间的区别,一个用于引用,另一个用于对象。我还知道python在启动时会缓存范围为(-5, 256)
的整数,所以在将它们与is
进行比较时,它们应该可以工作。
然而,我看到了一种奇怪的行为。
>>> 2**7 is 2**7
True
>>> 2**10 is 2**10
False
这是意料之中的,2**7
是128
,2**10
是1024
,一个在区间(-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