当python添加小整数时,幕后会发生什么?

当python添加小整数时,幕后会发生什么?,python,integer,cpython,Python,Integer,Cpython,我最近在玩弄id,意识到(c?)Python做了一些非常明智的事情:它确保小int始终具有相同的id >>> a, b, c, d, e = 1, 2, 3, 4, 5 >>> f, g, h, i, j = 1, 2, 3, 4, 5 >>> [id(x) == id(y) for x, y in zip([a, b, c, d, e], [f, g, h, i, j])] [True, True, True, True, True] 但

我最近在玩弄
id
,意识到(c?)Python做了一些非常明智的事情:它确保小int始终具有相同的
id

>>> a, b, c, d, e = 1, 2, 3, 4, 5
>>> f, g, h, i, j = 1, 2, 3, 4, 5
>>> [id(x) == id(y) for x, y in zip([a, b, c, d, e], [f, g, h, i, j])]
[True, True, True, True, True]
但我突然想到,数学运算的结果是否也是如此。结果是:

>>> nines = [(x + y, 9) for x, y in enumerate(reversed(range(10)))]
>>> [id(x) == id(y) for x, y in nines]
[True, True, True, True, True, True, True, True, True, True]
似乎它在n=257时开始失败

>>> a, b = 200 + 56, 256
>>> id(a) == id(b)
True
>>> a, b = 200 + 57, 257
>>> id(a) == id(b)
False
但有时,即使数字更大,它仍然有效:

>>> [id(2 * x + y) == id(300 + x) for x, y in enumerate(reversed(range(301)))][:10]
[True, True, True, True, True, True, True, True, True, True]

这是怎么回事?python是如何做到这一点的?

AFAIK,id与参数的大小无关。它必须返回一个生命周期唯一标识符,如果两个不同的参数不同时存在,它可以为它们返回相同的结果。

Python以一定的数字保存一个
int
对象池。当您在该范围内创建一个时,实际上会得到对先前存在的一个的引用。我怀疑这是出于优化的原因

对于超出该池范围的数字,无论何时尝试创建一个新对象,都会返回一个新对象

$ python
Python 3.2 (r32:88445, Apr 15 2011, 11:09:05) 
[GCC 4.5.2 20110127 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 300
>>> id(x)
140570345270544
>>> id(100+200)
140570372179568
>>> id(x*2)
140570345270512
>>> id(600)
140570345270576

PyObject*PyInt_FromLong(long-ival) 返回值:新引用。创建一个 值为的新整数对象 伊瓦尔

当前的实现保持了 所有对象的整数对象数组 如果选择-5和256之间的整数 在指定的范围内创建一个int 事实上,只需要回一个参考 现有对象。因此它应该是 可以更改1的值。我 怀疑Python在中的行为 此案例未定义。:-)


强调我的

你陷入了一个不寻常的陷阱:

id(2 * x + y) == id(300 + x)
这两个表达式
2*x+y
300+x
没有重叠的生存期。这意味着Python可以计算左侧,获取其id,然后在计算右侧之前释放整数。当CPython释放一个整数时,它会将其放在一个释放的整数列表中,然后在下次需要时将其重新用于另一个整数。因此,即使计算结果非常不同,您的ID也会匹配:

>>> x, y = 100, 40000
>>> id(2 * x + y) == id(300 + x)
True
>>> 2 * x + y, 300 + x
(40200, 400)

从文档:返回对象的“标识”。这是一个整数(或长整数),保证该对象在其生存期内唯一且不变。两个生命周期不重叠的对象可能具有相同的id()值。@Daenyth:请指定不正确的内容。呃,为什么删除注释而不解释它呢?一个(或部分或全部)python实现为某些少量整数保留一个数组的事实并不影响id()的工作方式。谁知道在其他或未来的实现中会不会是这样呢?我们不应该依赖于实现细节,而应该依赖于文档化的API来避免不好的意外。API声明了唯一性和一致性或不重叠的对象,没有其他内容。对于int的某些值,它会产生相同的输出,这很好,但这只是偶然的(由于您正在使用的当前实现)。再看看投票结果,我不知道为什么会有这么多人投了反对票。这里有两种行为,第一种是(实现定义的)特定整数对象的缓存,第二种是重用ID的可能性。Hyperboreus(AFAIU)正确地指出,在两个不同的对象上看到
id()
的相同结果,在对象具有非重叠生命期的情况下,这似乎毫无意义。邓肯的回答基本上就是这么说的,尽管无可否认,措辞没有那么清晰。这不是重点,每个人都有权在他们认为合适的时候这样做。重要的不是将代码基于随意的实现行为,而是基于文档化的API。顺便说一句,这是一个非常有趣的问题,有很多有趣的答案和评论。当数字较大时会发生什么?有时ID还是一样的。它是在做散列查找还是什么?@jsau:我编辑了我的答案以包含它。@Daenyth,是的,但有时它不是一个新对象;正如我的示例所示,有时
2*x+y
返回与
300+x
相同的对象。还是我误解了
id
的作用?@jsau:我没看到。我不能确定在这种情况下会发生什么,但我确实发布了一个示例,支持您重新获得一个新对象
id()
返回对象的唯一标识符,在cpython中,该标识符是对象在内存中的地址。如果id指向的对象被垃圾回收,则id将被重用。所以你不能把id作为伪对象密钥,它们可能会在以后指向不同的东西。好的,这是有道理的。谢谢因此,如果您上面所说的是真的,那么就有一种感觉,python int是可变的(只有在被垃圾收集之后)。