Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 内存中列表的大小_Python_Python 3.x - Fatal编程技术网

Python 内存中列表的大小

Python 内存中列表的大小,python,python-3.x,Python,Python 3.x,我刚刚在内存中试验了python数据结构的大小。我写了以下片段: import sys lst1=[] lst1.append(1) lst2=[1] print(sys.getsizeof(lst1), sys.getsizeof(lst2)) 我在以下配置上测试了代码: Windows 7 64位,Python3.1:输出为:5240,因此lst1有52个字节,lst2有40个字节 使用Python3.2的Ubuntu 11.4 32位:输出为48 32 Ubuntu 11.4 32位P

我刚刚在内存中试验了python数据结构的大小。我写了以下片段:

import sys
lst1=[]
lst1.append(1)
lst2=[1]
print(sys.getsizeof(lst1), sys.getsizeof(lst2))
我在以下配置上测试了代码:

  • Windows 7 64位,Python3.1:输出为:
    5240
    ,因此lst1有52个字节,lst2有40个字节
  • 使用Python3.2的Ubuntu 11.4 32位:输出为
    48 32
  • Ubuntu 11.4 32位Python 2.7:
    48 36
有人能解释一下为什么这两个尺寸不同,尽管都是包含1的列表吗


在getsizeof函数的python文档中,我发现了以下内容:
…如果对象由垃圾收集器管理,则会增加垃圾收集器开销。
在我的小示例中是否会出现这种情况?

对不起,前面的评论有点简短

现在发生的事情是,您正在查看列表是如何分配的(我想您可能只是想看看事情有多大——在这种情况下,请使用
sys.getsizeof()

在列表中添加内容时,可能会发生以下两种情况之一:

  • 多余的物品放在空余的地方

  • 需要额外的空间,所以会制作一个新的列表,复制内容,并添加额外的内容

  • 由于(2)是昂贵的(复制东西,甚至指针,所需时间与要复制的东西的数量成比例,因此随着列表变大而增长),我们希望不经常这样做。因此,我们不只是增加一点空间,而是增加了一整块。通常,添加量的大小与已经使用的量相似——这样数学计算得出,分配内存的平均成本(分布在多次使用中)只与列表大小成比例

    所以你所看到的与这种行为有关。我不知道确切的细节,但如果
    []
    [1]
    (或两者)都是特殊情况,只分配了足够的内存(在这些常见情况下可以节省内存),然后追加上面描述的“抓取新块”会增加更多内存,我也不会感到惊讶

    但我不知道确切的细节——这就是动态数组通常的工作方式。python中列表的精确实现将被精细地调整,以使其适合典型的python程序。所以我真正想说的是,你不能相信一个列表的大小来告诉你它到底包含了多少——它可能包含额外的空间,而额外的可用空间的数量很难判断或预测

    ps一个简洁的替代方法是将列表作为
    (值,指针)
    对,其中每个指针指向下一个元组。通过这种方式,您可以递增地增加列表,尽管使用的总内存更高。这是一个链表(python使用的更像是向量或动态数组)


    [更新]见Eli的精彩答案。他/她解释说,
    []
    [1]
    都是精确分配的,但是添加到
    []
    后会分配额外的块。代码中的注释就是我上面所说的(这称为“过度分配”,金额与我们拥有的部分成比例,因此平均(“摊销”)成本与大小成比例)。

    这里有一个更完整的交互式会话,它将帮助我解释发生了什么(Windows XP 32位上的Python 2.6,但实际上并不重要):

    请注意,空列表比其中包含
    [1]
    的列表小一些。然而,当添加元素时,它会变得更大

    原因是CPython源代码中
    Objects/listobject.c
    中的实现细节

    空列表 创建空列表时,
    []
    不会为元素分配空间-这可以在
    PyList\u New
    中看到。36字节是32位计算机上列表数据结构本身所需的空间量

    包含一个元素的列表 创建包含单个元素的列表时,除了列表数据结构本身所需的内存外,还会为一个元素分配空间。同样,这可以在
    PyList\u New
    中找到。给定
    size
    作为参数,它计算:

    nbytes = size * sizeof(PyObject *);
    
    然后是:

    if (size <= 0)
        op->ob_item = NULL;
    else {
        op->ob_item = (PyObject **) PyMem_MALLOC(nbytes);
        if (op->ob_item == NULL) {
            Py_DECREF(op);
            return PyErr_NoMemory();
        }
        memset(op->ob_item, 0, nbytes);
    }
    Py_SIZE(op) = size;
    op->allocated = size;
    
    让我们做些数学题 让我们看看我在文章开头的会话中引用的数字是如何达到的

    因此36字节是列表数据结构本身在32位上所需的大小。对于单个元素,为一个指针分配了空间,因此额外增加了4个字节—总共40个字节。好的,到目前为止

    当对空列表调用
    app1
    时,它将使用
    size=1
    调用
    list\u resize
    。根据
    list\u resize
    的过度分配算法,1之后的下一个最大可用大小是4,因此将为4个指针分配位置。4*4=16字节,36+16=52


    事实上,一切都是有道理的:-)

    下面是列表增长模式的快速演示。更改range()中的第三个参数将更改输出,使其看起来不像listobject.c中的注释,但仅添加一个元素时的结果似乎非常准确

    allocated = 0
    for newsize in range(0,100,1):
        if (allocated < newsize):
            new_allocated = (newsize >> 3) + (3 if newsize < 9 else 6)
            allocated = newsize + new_allocated;
        print newsize, allocated
    
    allocated=0
    对于范围(0100,1)内的新闻大小:
    如果(已分配<新闻大小):
    新分配=(新闻大小>>3)+(如果新闻大小<9,则为3,否则为6)
    已分配=新闻大小+已分配的新内容;
    打印新闻大小,分配
    
    公式根据系统架构进行更改 (尺寸-36)/4,适用于32位机器和 (大小为64)/8,适用于64位计算机

    36,64-基于机器的空列表的大小
    4,8-基于机器的列表中单个元素的大小

    列表不是以增量方式分配的,而是以“块”的形式分配的(并且块随着列表变大而变大)。这是必要的,以便附加数据的摊销成本较低。因此,我猜分配器在这两种情况下的工作方式不同。但事实上,你为什么这么关心列表是如何分配的呢?如果你需要知道某物的大小,可以使用sys.getsizeof()。@andrew cooke:请把它当作
    /* This over-allocates proportional to the list size, making room
    * for additional growth.  The over-allocation is mild, but is
    * enough to give linear-time amortized behavior over a long
    * sequence of appends() in the presence of a poorly-performing
    * system realloc().
    * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
    */
    new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
    
    /* check for integer overflow */
    if (new_allocated > PY_SIZE_MAX - newsize) {
        PyErr_NoMemory();
        return -1;
    } else {
        new_allocated += newsize;
    }
    
    allocated = 0
    for newsize in range(0,100,1):
        if (allocated < newsize):
            new_allocated = (newsize >> 3) + (3 if newsize < 9 else 6)
            allocated = newsize + new_allocated;
        print newsize, allocated