【问题标题】:Why isn't the size of an empty list 0 bytes?为什么空列表的大小不是 0 字节?
【发布时间】:2017-03-30 14:04:02
【问题描述】:

今天自己在Python 2.7.13中运行了下面给出的代码,发现列表为空时大小不为0:

import sys
data = []
for k in range(n):
    a = len(data)
    b = sys.getsizeof(data)
    print('Length:{0:3d};Size in bytes:{1:4d}'.format(a,b))
    data.append(None)

我的机器上的输出:

Length: 0; Size in bytes : 72
Length: 1; Size in bytes : 104
Length: 2; Size in bytes : 104
Length: 3; Size in bytes : 104
Length: 4; Size in bytes : 104
Length: 5; Size in bytes : 136
Length: 6; Size in bytes : 136
Length: 7; Size in bytes : 136
Length: 8; Size in bytes : 136
Length: 9; Size in bytes : 200
Length: 10; Size in bytes : 200
Length: 11; Size in bytes : 200
Length: 12; Size in bytes : 200
Length: 13; Size in bytes : 200
Length: 14; Size in bytes : 200
Length: 15; Size in bytes : 200
Length: 16; Size in bytes : 200
Length: 17; Size in bytes : 272
Length: 18; Size in bytes : 272
Length: 19; Size in bytes : 272

我想知道为什么会这样?

似乎 Python 正在为某些东西保留内存。 那是什么东西??

【问题讨论】:

标签: python python-2.7 list python-3.x python-internals


【解决方案1】:

因为从sys.getsizeof 返回的列表大小不仅包括列表包含的元素。

Python 中的每个对象都由C-struct 表示;这个结构包含指向使列表成为列表的所有事物的指针(主要是它的方法)。调用sys.getsizeof 时也会考虑到这一点。

您可以随时查看 GitHub 上 CPython 存储库主分支中的 implementation of list.__sizeof__

static PyObject *
list___sizeof___impl(PyListObject *self)
{
    Py_ssize_t res;

    res = _PyObject_SIZE(Py_TYPE(self)) + self->allocated * sizeof(void*);
    return PyLong_FromSsize_t(res);
}

(删除不相关的 arg 临床输出。)

sizeoffunction for 2.x 做同样的事情。

返回值res还包括列表对象类型_PyObject_SIZE(Py_Type(self))的大小。

由于 Python 中的一切都是对象,因此这种行为随处可见,例如,整数 0

>>> getsizeof(0)
24

虽然您通常不会想到这一点,但当您意识到 Python 中的所有内容都有“额外的包袱”时,这完全有道理,这使得我们认为理所当然的行为成为可能。

【讨论】:

    【解决方案2】:

    Python 是用 C 实现的,因此会将数据存储在 C 结构中。

    请记住,所有事物都是“对象”——对象必须具有类型和对象大小,即使它们不存储任何东西

    下面是PyObject_VAR_HEADPyListObject C 数据类型。

    #define PyObject_VAR_HEAD               \
        PyObject_HEAD                       \
        Py_ssize_t ob_size; /* Number of items in variable part */
    
    typedef struct {
        PyObject_VAR_HEAD
        /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
        PyObject **ob_item;
    
        /* ob_item contains space for 'allocated' elements.  The number
         * currently in use is ob_size.
         * Invariants:
         *     0 <= ob_size <= allocated
         *     len(list) == ob_size
         *     ob_item == NULL implies ob_size == allocated == 0
         * list.sort() temporarily sets allocated to -1 to detect mutations.
         *
         * Items must normally not be NULL, except during construction when
         * the list is not yet visible outside the function that builds it.
         */
        Py_ssize_t allocated;
    } PyListObject;
    

    请记住,sys.getsizeof() 将返回底层内存使用情况,而不是您真正需要从 Python 中考虑或担心的事情:

    以字节为单位返回对象的大小。

    只考虑直接归因于对象的内存消耗,而不考虑它所引用的对象的内存消耗。

    此外,正如您的测试所示,还有大量的预分配正在进行。 每次调用append()时,新内存与list无关。

    【讨论】:

      猜你喜欢
      • 2017-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-30
      • 2013-03-26
      • 1970-01-01
      • 2021-04-06
      相关资源
      最近更新 更多