27

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

>>> 1000 is 10**3
False

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

>>> 1000 is 1000
True

因此,也许 Python 对计算的处理与“普通”整数不同。但这个假设也不正确:

>>> 1 is 1**2
True

如何解释这种行为?

4

1 回答 1

43

这里发生了两件不同的事情: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 ** 31000,但该优化不会重用预先存在的常量。结果,LOAD_CONST操作码在索引 0 和 3 处加载了两个不同的整数对象,它们是两个不同 int的对象。

然后在小整数被实习的地方进行了优化;1在 Python 程序的生命周期内只创建了一个对象的副本;这适用于 -5 到 256 之间的所有整数。

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

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

于 2014-02-19T12:22:03.943 回答