为什么Python处理'1是1 ** 2'与'1000是10 ** 3'不同?

时间:2014-02-19 12:20:17

标签: python reference semantics python-internals

this关于缓存小整数和字符串的问题的启发,我发现了以下我不理解的行为。

>>> 1000 is 10**3
False

我以为我理解了这种行为:1000是很大的缓存。 1000和10 ** 3指向2个不同的对象。但我错了:

>>> 1000 is 1000
True

因此,Python可能会将计算与“正常”整数区别对待。但这种假设也不正确:

>>> 1 is 1**2
True

如何解释这种行为?

1 个答案:

答案 0 :(得分:42)

这里有两个不同的东西:Python存储int文字(和其他文字)作为常量,编译后的字节码小整数对象被缓存为单例。

当你运行1000 is 1000时,只存储和重用一个这样的常量。你真的在看同一个对象:

>>> import dis
>>> compile('1000 is 1000', '<stdin>', 'eval').co_consts
(1000,)
>>> dis.dis(compile('1000 is 1000', '<stdin>', 'eval'))
  1           0 LOAD_CONST               0 (1000) 
              3 LOAD_CONST               0 (1000) 
              6 COMPARE_OP               8 (is) 
              9 RETURN_VALUE         

这里LOAD_CONST指的是索引0处的常量;您可以在字节码对象的.co_consts属性中看到存储的常量。

将此与1000 is 10 ** 3案例进行比较:

>>> compile('1000 is 10**3', '<stdin>', 'eval').co_consts
(1000, 10, 3, 1000)
>>> dis.dis(compile('1000 is 10**3', '<stdin>', 'eval'))
  1           0 LOAD_CONST               0 (1000) 
              3 LOAD_CONST               3 (1000) 
              6 COMPARE_OP               8 (is) 
              9 RETURN_VALUE         

有一个窥孔优化在编译时预先计算常量上的表达式,并且此优化已将10 ** 3替换为1000,但优化不会重复使用预先存在的常量。因此,LOAD_CONST操作码正在加载两个不同的整数对象,索引为0和3,这些是两个不同的 int对象。

然后有small integers are interned的优化措施;在Python程序的生命周期中,只创建了1对象的一个​​副本;这适用于-5到256之间的所有整数。

因此,对于1 is 1**2情况,Python内部使用内部缓存中的单个int()对象。这是一个CPython实现细节。

这个故事的寓意是,当你真的想按价值进行比较时,你永远不应该使用is。始终使用==作为整数。