我最近在摆弄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是如何做到这一点的?
您陷入了一个常见的陷阱:
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
对象池。当你在这个范围内创建一个时,你实际上得到了对预先存在的引用。我怀疑这是出于优化的原因。
对于该池范围之外的数字,无论何时尝试创建一个新对象,都会得到一个新的对象。
$ 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)返回值:新建引用。创建值为的新整数对象ival。
当前实现保持所有的整数对象数组介于-5和256之间的整数,当在该范围内创建一个int实际上,只需要返回一个引用现有对象所以应该是可以改变1的值。我怀疑Python在此情况未定义。:-)
emphasis mine
AFAIK,id与参数的大小无关。它必须返回一个终身唯一标识符,如果两个不同的参数不同时存在,它可以为它们返回相同的结果。