列表的内存大小 python

Memory Size of list python

我正在用列表做一些试验,然后我发现了自引用列表。我搜索了 SO 并得到了一些关于它的基本问题的回答。但是当我尝试获取不同长度的自引用列表的内存大小时,我发现了一个有趣的模式

重现代码:

import sys

memory_size = {}

for length in range(50):
    lst = []
    for length_loop in range(length):
        lst.append(lst)
    memory_size[length] = sys.getsizeof(lst)

memory_size 的值:

{0: 64, 1: 96, 2: 96, 3: 96, 4: 96, 5: 128, 6: 128, 7: 128, 8: 128, 9: 192, 10: 192, 11: 192, 12: 192, 13: 192, 14: 192, 15: 192, 16: 192, 17: 264, 18: 264, 19: 264, 20: 264, 21: 264, 22: 264, 23: 264, 24: 264, 25: 264, 26: 344, 27: 344, 28: 344, 29: 344, 30: 344, 31: 344, 32: 344, 33: 344, 34: 344, 35: 344, 36: 432, 37: 432, 38: 432, 39: 432, 40: 432, 41: 432, 42: 432, 43: 432, 44: 432, 45: 432, 46: 432, 47: 528, 48: 528, 49: 528}

绘制上述数据点

Python 3.7.3 (default, Mar 27 2019, 16:54:48)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.

为什么自引用列表的内存大小在一定长度范围内保持不变,并在一定长度后增加?

而且内存大小的增加也是不同的

列表 总是 如果它们是通过追加增长的,则显示此模式。

要理解的一个关键点,sys.getsizeof 不考虑列表中引用的 objects,只考虑列表的大小 object 本身。现在,Python list objects 被实现为引擎盖下的数组列表,所以本质上有一个 PyObject header (比如,16 字节开销或类似的),然后PyObject 指针的原始数组(为什么 它们可以是异构的,并引用它们自己)。

这个底层数组是过度分配的,它是re-sized以保证分摊constant-time.append操作的方式。 换句话说,Python list object 已经摊销了 constant-time .append,所以像 for x in range(N): my_list.append(0) 这样的事情是 线性的时间操作,因为基础缓冲区不是每次迭代re-allocated。

看,你会看到与任何 object 相同的模式,例如 None:

In [24]: import sys
    ...:
    ...: memory_size = {}
    ...:
    ...: for length in range(50):
    ...:     lst = []
    ...:     for length_loop in range(length):
    ...:         lst.append(None)
    ...:     memory_size[length] = sys.getsizeof(lst)
    ...:

In [25]: memory_size
Out[25]:
{0: 72,
 1: 104,
 2: 104,
 3: 104,
 4: 104,
 5: 136,
 6: 136,
 7: 136,
 8: 136,
 9: 200,
 10: 200,
 11: 200,
 12: 200,
 13: 200,
 14: 200,
 15: 200,
 16: 200,
 17: 272,
 18: 272,
 19: 272,
 20: 272,
 21: 272,
 22: 272,
 23: 272,
 24: 272,
 25: 272,
 26: 352,
 27: 352,
 28: 352,
 29: 352,
 30: 352,
 31: 352,
 32: 352,
 33: 352,
 34: 352,
 35: 352,
 36: 440,
 37: 440,
 38: 440,
 39: 440,
 40: 440,
 41: 440,
 42: 440,
 43: 440,
 44: 440,
 45: 440,
 46: 440,
 47: 536,
 48: 536,
 49: 536}

为了说服您,这是您的 self-referening 列表:

In [26]: import sys
    ...:
    ...: memory_size = {}
    ...:
    ...: for length in range(50):
    ...:     lst = []
    ...:     for length_loop in range(length):
    ...:         lst.append(lst)
    ...:     memory_size[length] = sys.getsizeof(lst)
    ...:

In [27]: memory_size
Out[27]:
{0: 72,
 1: 104,
 2: 104,
 3: 104,
 4: 104,
 5: 136,
 6: 136,
 7: 136,
 8: 136,
 9: 200,
 10: 200,
 11: 200,
 12: 200,
 13: 200,
 14: 200,
 15: 200,
 16: 200,
 17: 272,
 18: 272,
 19: 272,
 20: 272,
 21: 272,
 22: 272,
 23: 272,
 24: 272,
 25: 272,
 26: 352,
 27: 352,
 28: 352,
 29: 352,
 30: 352,
 31: 352,
 32: 352,
 33: 352,
 34: 352,
 35: 352,
 36: 440,
 37: 440,
 38: 440,
 39: 440,
 40: 440,
 41: 440,
 42: 440,
 43: 440,
 44: 440,
 45: 440,
 46: 440,
 47: 536,
 48: 536,
 49: 536}

个体大小的差异归结为 Python 版本和系统架构(例如,在 32 位系统上,指针是 4 个字节而不是 8 个字节,并且 Python 版本可以自由更改实现细节,例如空列表的大小)。注意,以上是 Python 3.7 上的 运行,如果我使用另一个环境:

(base) juanarrivillaga@173-11-109-137-SFBA ~ % python -c "import sys; print(f'{sys.version}\nEmpty List Size: {sys.getsizeof([])}')"
3.8.1 (default, Jan  8 2020, 16:15:59)
[Clang 4.0.1 (tags/RELEASE_401/final)]
Empty List Size: 56

答案可能就在其中。显然,大小的差异将取决于您的系统架构和 python 安装,增长大小可以通过 Python.

中列表的实现来解释

https://github.com/python/cpython/blob/master/Objects/listobject.c