写点什么

浅析 Python3 列表操作之 * 和 *=

用户头像
王坤祥
关注
发布于: 2020 年 08 月 12 日
浅析Python3列表操作之*和*=

初学 Python 时总是会将a*=n理解为a=a*n,稍微深入后就会知道在 Python 中的不同,其中*调用__mul__ ,而*=调用__imul__


对于 list 对象也支持乘法操作,截止到 Python3.7 版本,上述仍然是成立的。我们知道 list 是由 C 实现的,所以真正的底层调用肯定也是 C 的实现。观察 list 对象的 C 实现的源码我们会知道乘法*操作调用list_repeat*=会调用list_inplace_repeat,下面分别看一下两者的 C 实现方式。

▍* 调用 list_repeat

static PyObject *list_repeat(PyListObject *a, Py_ssize_t n){    ...    size = Py_SIZE(a) * n;    if (size == 0)        return PyList_New(0);    np = (PyListObject *) list_new_prealloc(size);    ...    return (PyObject *) np;}
复制代码

从以上可以看出,list_repeat方法需要多少空间就申请多少空间,该操作返回的一个新的列表对象。


▍*= 调用 list_inplace_repeat

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;    ...}
static intlist_resize(PyListObject *self, Py_ssize_t newsize){ if (allocated >= newsize && newsize >= (allocated >> 1)) { assert(self->ob_item != NULL || newsize == 0); Py_SIZE(self) = newsize; return 0; } ... new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6); ...}
复制代码


list_inplace_repeat代码中通过调用list_resize来进行扩容,并告诉它这个列表需要容纳size*n个元素。从list_resize代码来看,当allocated空间足够时,不会进行扩容操作。


新申请的空间总是比所需要的大的如果进行 pop 等减小 list 元素数量的操作来看,实际上列表的大小也会按照相应策略进行缩减操作。


If the newsize falls lower than half the allocated size, then proceed with the realloc() to shrink the list.

--- From cpython


▍总结

  • *=会调用list_resize,可能会引起 list 空间扩容的情况,而且此时 list 对象占用空间会比实际 list 对象中元素占用空间大。

  • *会按需获取申请空间大小,不会调用list_resize方法。


好好学习,天天向上!

发布于: 2020 年 08 月 12 日阅读数: 83
用户头像

王坤祥

关注

日拱一卒,功不唐捐。 2017.10.17 加入

不懂热能的低温工程师不是好程序猿

评论

发布
暂无评论
浅析Python3列表操作之*和*=