为什么(0-6)是 -6 = False?

可能的复制品:
对于整数 ,Python 运算符的行为出乎意料

今天我尝试调试我的项目,经过几个小时的分析,我得到了这个:

>>> (0-6) is -6
False

但是,

>>> (0-5) is -5
True

你能解释一下为什么吗? 也许这是某种错误或者非常奇怪的行为。

> Python 2.7.3 (default, Apr 24 2012, 00:00:54) [GCC 4.7.0 20120414 (prerelease)] on linux2
>>> type(0-6)
<type 'int'>
>>> type(-6)
<type 'int'>
>>> type((0-6) is -6)
<type 'bool'>
>>>
5389 次浏览

从 -5到256的所有整数都缓存为与 CPython 共享相同地址的全局对象,因此 is测试通过。

这个构件在 http://www.laurentluce.com/posts/python-integer-objects-implementation/中有详细的解释,我们可以检查 http://hg.python.org/cpython/file/tip/Objects/longobject.c中当前的源代码。

一个特定的结构用于引用小整数并共享它们,因此访问速度很快。它是一个由262个指向整数对象的指针组成的数组。这些整数对象在初始化过程中被分配到我们上面看到的整数对象块中。小整数的范围从 -5到256。许多 Python 程序花费大量时间使用该范围内的整数,因此这是一个明智的决定。

这只是 CPython 的一个实现细节,您不应该依赖于它。例如,PyPy实现了整数的 id来返回它自己,所以即使它们在内部是“不同的对象”,(0-6) is -6也总是为真; 它还允许您配置是否启用这个整数缓存,甚至设置下限和上限。但是一般来说,从不同来源获取的物体不会是相同的。如果要比较相等性,只需使用 ==

这不是一个错误。 is不是一个相等测试。 ==将给出预期的结果。

这种行为的技术原因是,Python 实现可以将具有相同常量值的不同实例视为相同的对象或不同的对象。出于节省内存的原因,您正在使用的 Python 实现选择让某些小常量共享相同的对象。您不能指望这种行为在不同版本之间或不同的 Python 实现之间是相同的版本。

Python 在解释器中存储范围为 -5-256的整数: 它有一个整数对象池,从中返回这些整数。这就是为什么这些物体是相同的: (0-5)-5,但不是 (0-6)-6,因为它们是在现场创建的。

下面是 CPython 源代码中的源代码:

#define NSMALLPOSINTS           257
#define NSMALLNEGINTS           5
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

(查看 CPython 源代码: /trunk/Objects/intobject.c)。源代码包括以下注释:

/* References to small integers are saved in this array so that they
can be shared.
The integers that are saved are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/

然后,is操作符将比较它们(-5) ,因为它们是相同的对象(相同的内存位置) ,但是另外两个新整数(-6)将位于不同的内存位置(然后 is将不返回 True)。请注意,上面源代码中的 257是用于正整数的,因此它是 0 - 256(包含)。

(来源)

之所以会发生这种情况,是因为 CPython 缓存了一些小整数和小字符串,并为该对象的每个实例提供了相同的 id()

(0-5)-5对于 id()具有相同的值,而对于 0-6-6则不是这样

>>> id((0-6))
12064324
>>> id((-6))
12064276
>>> id((0-5))
10022392
>>> id((-5))
10022392

字符串也是如此:

>>> x = 'abc'
>>> y = 'abc'
>>> x is y
True
>>> x = 'a little big string'
>>> y = 'a little big string'
>>> x is y
False

有关字符串缓存的详细信息,请参阅: 在比较字符串和空格时,is运算符的行为不同