Python 3.6.5“is”和“==”用于超出缓存间隔的整数

Rar*_*ima 5 python compare reference object

我想先说一下,我知道两者之间的区别==,is一个用于引用,另一个用于对象。我也知道 python(-5, 256)在启动时缓存范围内的整数,因此在将它们与is.

但是,我看到了一种奇怪的行为。

>>> 2**7 is 2**7
True
>>> 2**10 is 2**10
False
Run Code Online (Sandbox Code Playgroud)

这是可以预料的,2**7is128和2**10is 1024,一个在区间内(-5, 256),另一个不在。

然而...

>>> 10000000000000000000000000000000000000000 is 10000000000000000000000000000000000000000
True
Run Code Online (Sandbox Code Playgroud)

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

Ara*_*Fey 3

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

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

你的两个操作数都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
Run Code Online (Sandbox Code Playgroud)

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

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

>>> x = 999
>>> x is 999
False
Run Code Online (Sandbox Code Playgroud)