在调试一些代码时,我发现了一些奇怪的东西。 显然,
>>> (0-6) is -6
False
但是,
>>> (0-5) is -5
True
为什么会发生这种情况?
从-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
始终为真,即使它们在内部是“不同的对象”;它还允许您配置是否启用此整数缓存,甚至设置下限和上限。但是通常来说,从不同来源检索到的对象将不相同。如果您想比较相等性,请使用==
。
range(m, n)
表示[m, n)的整数区间,即m,m + 1,m + 2,...,n - 1。它不包括n,因此range(-5,257)
不包含257,所以对于这个范围描述的行为是正确的。 - 0xc0de(0-5)
和-5
,但不是(0-6)
和-6
,因为它们是即时创建的。#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
(包括)。这不是一个错误。 is
不是一个等式测试,==
才会给出期望的结果。
这种行为的技术原因是,Python 实现可以自由地将相同常量值的不同实例视为相同对象或不同对象。您正在使用的 Python 实现选择使某些小常量共享相同的对象,以节省内存。您不能依赖于此行为在版本之间或跨不同的 Python 实现中相同。
is
不是一个相等性测试。这个 is
是一个身份测试,用于查看两个对象是否完全相同。恰好在 CPython 实现中,一些 int 对象被缓存了。 - Casey Kuball这是因为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
operator behaves differently when comparing strings with spaces
-6
被认为是“大”的,而-5
不是呢?什么是被认为是“大”的资格标准? - inspectorG4dget
is
?除了is/is not None
的情况外,在Python中通常不应经常使用它。 - Russell Borogove=
一样运作,这是一个错误的期望。 - LarsH