列表预分配空间的锅

栏目: Python · 发布时间: 6年前

内容简介:两个存储元素内容相同的列表占用内存空间可能会不一样。甚至

起步

两个存储元素内容相同的列表占用内存空间可能会不一样。

列表预分配空间的锅

甚至 a == b 都是成立的。是什么导致了占用的空间不一致的呢?

列表对象的存储方式

Python 中 list 的实现方式和 C++ 的 vector 类似,它并不是存多少东西就申请多少内存,它会申请一块较大的内存,避免每次新增元素都要进行内存申请和元素拷贝。当空间不足以容纳新元素时会进行扩容。

列表预分配空间的锅

上图中 [0] 时是确定的元素个数,就只申请容纳一个元素空间,而 append 追加的方式会导致 list 对象扩容。

解答

为了解释最开始图片里的问题,还要先知道乘法 **= 是两个不同的操作符。这点上可以通过字节码来得到:

import dis
def fun():
    a = [0]
    a = a * 10
    b = [0]
    b *= 10

dis.dis(fun)

相关的字节码为:

6 LOAD_FAST                0 (a)
 8 LOAD_CONST               2 (10)
10 BINARY_MULTIPLY          # * 操作符
12 STORE_FAST               0 (a)
...
20 LOAD_FAST                1 (b)
22 LOAD_CONST               2 (10)
24 INPLACE_MULTIPLY         # *= 操作符
26 STORE_FAST               1 (b)

我们初学时总是会将 a *= n 理解为 a = a * n (当然这里理解是没错的),深入后就会发现他们的不同了。用 Python 中的魔术方法来说就是一个会调用 __mul__ 一个会调用 __imul__

而 list 对象是 C 实现的,自然是调用 C 函数了,乘法操作会调用 list_repeat()*= 会调用 list_inplace_repeat()

[listobject.c  v3.6.5]
static PyObject *
list_repeat(PyListObject *a, Py_ssize_t n)
{
    ...
    size = Py_SIZE(a) * n;
    if (size == 0)
        return PyList_New(0);
    np = (PyListObject *) PyList_New(size);  // 创建容纳size个空间的列表
    ...
}

从这可以看出 list_repeat 需要多少空间就申请多少空间,从这里也可以看出乘法操作是返回一个新的列表对象。

再来看看 list_inplace_repeat

[listobject.c  v3.6.5]
static PyObject *
list_inplace_repeat(PyListObject *self, Py_ssize_t n)
{
    ...
    size = PyList_GET_SIZE(self);
    ...
    if (list_resize(self, size*n) < 0)
        return NULL;
    ...
}

代码中试图通过 list_resize 来进行扩容,并告诉它这个列表需要容纳 size * n 个元素,那么 resize 函数里面会申请比所需的空间还要大点的内存吗?显然是的:

[listobject.c  v3.6.5]
static int
list_resize(PyListObject *self, Py_ssize_t newsize)
{
    ...
    new_allocated = newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
    ...
}

resize 后的空间 总是 比所需要的大的。按照这里的扩容规则,如果一个空列表通过 append 不断往里面添加元素,那么空间占用会是 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...

所以这个 *= 会引起列表 resize,而比 * 的方式占用空间大;解释完毕。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Google

Google

托马斯·舒尔茨(Thomas·Schulz) / 严孟然、陈琴 / 当代中国出版社 / 2016-11-1 / CNY 49.80

想要掌握未来,必须了解谷歌 1998年从车库起家,短短数年研发上千项专利,2016年力压苹果、亚马逊,成为“世界最具价值品牌”“最佳雇主”,谷歌无疑是互联网时代的最大赢家,这家公司有能力通过巨额广告利润收获现在,更有意愿在人工智能层面创造未来。 据说谷歌势不可挡,永不餍足。从互联网搜索到智能翻译再到地图导航,谷歌是我们通向世界的门户。不仅如此,就像管理全世界的数据流一样,谷歌还要在不久......一起来看看 《Google》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换