Python中低值整数在内存中的布局

6 投票
6 回答
1537 浏览
提问于 2025-04-15 18:52

我的问题是:这些模式(下面提到的)是从哪里来的?

我在某个地方学到,Python 对小整数有独特的“拷贝”,如果这个词用得对的话。比如:

>>> x = y = 0
>>> id(0)
4297074752
>>> id(x)
4297074752
>>> id(y)
4297074752
>>> x += 1
>>> id(x)
4297074728
>>> y
0

当我查看整数的内存位置时,早期有一个简单的模式:

>>> N = id(0)
>>> for i in range(5):
...     print i, N - id(i)
... 
0 0
1 24
2 48
3 72
4 96
>>> bin(24)
'0b11000'

我不太明白为什么选择这个增量。此外,我完全无法解释256以上的这个模式:

>>> prev = 0
>>> for i in range(270):
...     t = (id(i-1), id(i))
...     diff = t[0] - t[1]
...     if diff != prev:
...         print i-1, i, t, diff
...         prev = diff
... 
-1 0 (4297074776, 4297074752) 24
35 36 (4297073912, 4297075864) -1952
36 37 (4297075864, 4297075840) 24
76 77 (4297074904, 4297076856) -1952
77 78 (4297076856, 4297076832) 24
117 118 (4297075896, 4297077848) -1952
118 119 (4297077848, 4297077824) 24
158 159 (4297076888, 4297078840) -1952
159 160 (4297078840, 4297078816) 24
199 200 (4297077880, 4297079832) -1952
200 201 (4297079832, 4297079808) 24
240 241 (4297078872, 4297080824) -1952
241 242 (4297080824, 4297080800) 24
256 257 (4297080464, 4297155264) -74800
257 258 (4297155072, 4297155288) -216
259 260 (4297155072, 4297155336) -264
260 261 (4297155048, 4297155432) -384
261 262 (4297155024, 4297155456) -432
262 263 (4297380280, 4297155384) 224896
263 264 (4297155000, 4297155240) -240
264 265 (4297155072, 4297155216) -144
266 267 (4297155072, 4297155168) -96
267 268 (4297155024, 4297155144) -120

有没有什么想法、线索或者可以查阅的地方?

编辑:那24有什么特别之处?

更新:标准的 有个 sys.getsizeof() 方法,当我用 1 作为参数调用它时,它返回 24。这可真不少字节,但在64位机器上,我们每个类型、值和引用计数各占8个字节。此外,可以查看 这里,还有C API参考 这里

我花了一些时间查看了彼得·汉森在评论中提到的“源代码”链接。虽然没找到整数的定义(除了 *int_int 的声明),但我确实找到了:

#define NSMALLPOSINTS       257
#define NSMALLNEGINTS       5

6 个回答

2

我记得Python在内部会缓存小于256的整数副本,这样可以避免每次都创建新的Python对象,因为这些小整数是常用的。所以一旦你使用的整数超过256,Python就会每次创建新的对象,这些对象在内存中的位置看起来可能是“随机”的(当然,这些位置对Python的内存分配器来说是有意义的,但对我们来说可能就不太明白了)。

2

前256个整数是预先分配好的

>>> for n in range(1000):
>>>  if id(n) != id(n+0):
>>>   print n
>>>   break

257
8

低值的整数是提前分配好的,而高值的整数则是在计算时才分配。出现在源代码中的整数其实是同一个对象。在我的系统上,

>>> id(2) == id(1+1)
True
>>> id(1000) == id(1000+0)
False
>>> id(1000) == id(1000)
True

你还会注意到这些id是和系统有关的。它们实际上就是内存地址,是由系统的分配器(或者可能是链接器,针对静态对象?)分配的。

>>> id(0)
8402324

补充一下:id(1000) == id(1000)之所以成立,是因为Python编译器发现代码中有两个相同的整数常量,所以它只为这两个常量分配了一个对象。这样做在运行时会影响性能,但在编译时是不会被注意到的。(没错,解释器也是编译器。大多数解释器同时也是编译器,只有极少数不是。)

撰写回答