当 python 添加小整数时,幕后会发生啥? [复制]
Posted
技术标签:
【中文标题】当 python 添加小整数时,幕后会发生啥? [复制]【英文标题】:What happens behind the scenes when python adds small ints? [duplicate]当 python 添加小整数时,幕后会发生什么? [复制] 【发布时间】:2011-08-31 09:04:01 【问题描述】:我最近在摆弄 id
并意识到 (c?)Python 做了一些非常明智的事情:它确保小整数始终具有相同的 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是怎么做到的?
【问题讨论】:
【参考方案1】:你掉进了一个不常见的陷阱:
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)
【讨论】:
啊啊啊啊。好的,这是有道理的。谢谢! 所以如果你上面说的是真的,那么在某种意义上,python int 是可变的(只有在被垃圾收集之后)。【参考方案2】: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
Source
PyObject* PyInt_FromLong(long ival) 返回值:新引用。创建一个 新的整数对象,其值为 瓦尔。
当前的实现保持了一个 所有人的整数对象数组 -5 到 256 之间的整数,当你 在该范围内创建一个 int 实际上只是取回参考 现有的对象。所以它应该是 可以改变 1 的值。我 怀疑 Python 在 这种情况是未定义的。 :-)
强调我的
【讨论】:
数字越大会怎样?有时ID仍然相同。它是在做哈希查找还是什么? @jsau:我编辑了我的答案以包含它。 @Daenyth,是的,但有时它不是一个新对象;正如我的示例所示,有时2 * x + y
返回与300 + x
相同的对象。还是我误解了id
的作用?
@jsau:我没看到。我不能确定在这种情况下会发生什么,但我确实发布了一个支持你得到一个新对象的例子。 id()
返回一个对象的唯一标识符,在 cpython 中是该对象在内存中的地址。
如果 id 指向的对象被垃圾回收,他们将被重用。因此,您不能将 id 作为伪对象键保留,它们可能最终指向不同的东西。【参考方案3】:
AFAIK,id 与参数的大小无关。它必须返回一个终身唯一标识符,并且如果两个不同的参数不同时存在,它可以返回相同的结果。
【讨论】:
来自文档:返回对象的“身份”。这是一个整数(或长整数),保证该对象在其生命周期内是唯一且恒定的。具有不重叠生命周期的两个对象可能具有相同的 id() 值。 @Daenyth:请说明不正确之处。呃,你为什么要删除你的评论而不是解释它? 一个(或部分或全部)python 实现为某些少量整数保留一个数组这一事实不会影响 id() 的工作方式。谁能判断这在其他或未来的实现中是否会这样?不应依赖于实现细节,而应依赖于记录在案的 API 以避免意外。 API 声明了唯一性和恒定性或不重叠的对象,仅此而已。很高兴知道对于 int 的某些值它会产生相同的输出,但这只是偶然(由于您正在使用的当前实现)。另请查看赞成的答案。 我不知道为什么这会受到如此严重的反对。这里有两种行为,首先是某些整数对象的(实现定义的)缓存,其次是 id 被重用的可能性。 Hyperboreus 正确 (AFAIU) 指出,在两个不同的对象上看到来自id()
的相同结果根本没有任何意义在对象具有非重叠生命周期的情况下,似乎是这样。这基本上正是邓肯在上面的回答所说的,尽管措辞不那么清楚。
这不是重点,每个人都有权按照他们认为合适的方式这样做。重要的是不要将代码基于随意的实现行为,而是基于文档化的 API。顺便说一句,这是一个非常有趣的问题,有很多有趣的答案和 cmets。以上是关于当 python 添加小整数时,幕后会发生啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
iOS Safari Web 扩展 - 当我们添加新的所需权限时,当前用户会发生啥