我刚刚试验了内存中python数据结构的大小,我写了下面的代码片段:
import sys
lst1=[]
lst1.append(1)
lst2=[1]
print(sys.getsizeof(lst1), sys.getsizeof(lst2))
我在以下配置上测试了代码:
- Windows 7 64位,Python 3.1:输出为:
52 40
,因此LST1具有52字节,而LST2具有40字节。 - Ubuntu 11.4 32位版本,带Python 3.2:输出为
48 32
- Ubuntu 11.4 32位Python 2.7:
48 36
谁能给我解释一下为什么这两个大小不同,尽管它们都是包含1的列表?
在getsizeof函数的python文档中,我发现了以下内容:...adds an additional garbage collector overhead if the object is managed by the garbage collector.
在我的小例子中会是这种情况吗?
4条答案
按热度按时间amrnrhlw1#
下面是一个更完整的互动会话,它将帮助我解释正在发生的事情(Python 2.6在Windows XP 32位上,但这并不重要):
注意,空列表比包含
[1]
的列表要小一些,但是当添加了一个元素时,它会变得更大。其原因是CPython源代码中
Objects/listobject.c
的实现细节。空列表
当创建一个空列表
[]
时,没有为元素分配空间--这可以在PyList_New
中看到。36字节是32位机器上列表数据结构本身所需的空间量。包含一个元素的列表
当创建一个包含单个元素
[1]
的列表时,除了列表数据结构本身所需的内存之外,还为一个元素分配空间,这同样可以在PyList_New
中找到,给定size
作为参数,它计算:然后具有:
因此,我们看到
size = 1
为一个指针分配了4字节的空间(在我的32位机器上)。追加到空列表
在空列表上调用
append
时,会发生以下情况:PyList_Append
调用app1
app1
询问列表的大小(并得到0作为回答)app1
用size+1
调用list_resize
(在我们的例子中为1)list_resize
有一个有趣的分配策略,在这篇评论中从它的来源进行了总结。这就是:
让我们做一些数学
让我们看看我在文章开头的会话中引用的数字是如何达到的。
所以36字节是32位列表数据结构本身所需的大小,对于单个元素,空间被分配给一个指针,所以有4个额外的字节--总共40字节,到目前为止还不错。
当
app1
在空列表上被调用时,它用size=1
调用list_resize
。根据list_resize
的过度分配算法,1之后的下一个最大可用大小是4,因此将分配4个指针的位置。4 * 4 = 16字节,并且36 + 16 = 52。的确,一切都说得通:-)
wf82jlnq2#
抱歉,之前的评论有点草率。
实际情况是,您正在查看列表是如何分配的(我想您可能只是想查看列表有多大-在这种情况下,使用
sys.getsizeof()
)当向列表中添加某些内容时,可能发生以下两种情况之一:
1.多余的物品可以放在多余的空间里
1.需要额外的空间,所以制作了一个新的列表,将内容复制过来,并添加额外的内容。
因为(2)价格昂贵(复制东西,甚至指针,所花费的时间与要复制的东西的数量成比例,所以随着列表变大而增长)我们希望不频繁地这样做。所以不是仅仅添加多一点空间,而是添加整个块。通常添加的量的大小与已经在使用的量的大小相似-这样数学计算出分配存储器的平均成本,在许多使用中展开,仅与列表大小成比例。
所以你所看到的是与这个行为相关的。我不知道确切的细节,但是如果
[]
或[1]
(或者两者)是特殊情况,我不会感到惊讶,其中只分配了足够的内存(在这些常见情况下保存内存),然后appending执行上面描述的“抓取一个新块”,添加更多。但是我不知道确切的细节--这只是动态数组一般的工作方式。列表在python中的确切实现将被精细地调整,以便它对于典型的python程序是最优的。所以我真正要说的是,你不能相信列表的大小来告诉你它到底包含了多少--它可能包含额外的空间,并且额外空闲空间的量难以判断或预测。
ps一个简洁的替代方法是把列表做成
(value, pointer)
对,其中每个指针指向下一个元组。这样你就可以逐渐增加列表,尽管使用的总内存更高。这是一个链表(python使用的更像一个向量或动态数组)。[update]参见Eli的精彩回答。他们解释说
[]
和[1]
都是精确分配的,但是追加到[]
分配了额外的块。代码中的注解就是我上面所说的(这被称为“过度分配”,其数量与我们拥有的数量成比例,因此平均(“摊销”)成本与大小成比例)。5anewei63#
这里有一个列表增长模式的快速演示,改变range()中的第三个参数会改变输出,所以它看起来不像listobject. c中的注解,但是当简单地添加一个元素时,结果看起来非常准确。
e4yzc0pl4#
公式根据系统体系结构更改(size-36)/4(对于32位计算机)和(size-64)/8(对于64位计算机
36,64-基于计算机的空列表的大小4,8-基于计算机的列表中单个元素的大小