And*_*ely 148 python memory-management list python-internals
我创建了两个名单l1和l2,但每一个具有不同的创建方法:
import sys
l1 = [None] * 10
l2 = [None for _ in range(10)]
print('Size of l1 =', sys.getsizeof(l1))
print('Size of l2 =', sys.getsizeof(l2))
Run Code Online (Sandbox Code Playgroud)
但输出让我感到惊讶:
Size of l1 = 144
Size of l2 = 192
Run Code Online (Sandbox Code Playgroud)
使用列表推导创建的列表在内存中的大小更大,但是这两个列表在Python中是相同的.
这是为什么?这是CPython内部的一些东西,还是其他一些解释?
int*_*jay 158
当你编写时[None] * 10,Python知道它将需要一个恰好包含10个对象的列表,因此它会完全分配.
当您使用列表推导时,Python不知道它需要多少.因此,随着元素的添加,它逐渐增长.对于每次重新分配,它分配的空间比立即需要的多,因此不必为每个元素重新分配.结果列表可能比需要的要大一些.
比较使用相似大小创建的列表时,您可以看到此行为:
>>> sys.getsizeof([None]*15)
184
>>> sys.getsizeof([None]*16)
192
>>> sys.getsizeof([None for _ in range(15)])
192
>>> sys.getsizeof([None for _ in range(16)])
192
>>> sys.getsizeof([None for _ in range(17)])
264
Run Code Online (Sandbox Code Playgroud)
您可以看到第一种方法只分配所需的内容,而第二种方法则定期增长.在这个例子中,它为16个元素分配足够的元素,并且在到达17日时必须重新分配.
jua*_*aga 48
正如在这个问题中所指出的那样,list-comprehension list.append在引擎盖下使用,因此它将调用list-resize方法,该方法进行了全面的分配.
为了向您自己演示,您实际上可以使用disdissasembler:
>>> code = compile('[x for x in iterable]', '', 'eval')
>>> import dis
>>> dis.dis(code)
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x10560b810, file "", line 1>)
2 LOAD_CONST 1 ('<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_NAME 0 (iterable)
8 GET_ITER
10 CALL_FUNCTION 1
12 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x10560b810, file "", line 1>:
1 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 8 (to 14)
6 STORE_FAST 1 (x)
8 LOAD_FAST 1 (x)
10 LIST_APPEND 2
12 JUMP_ABSOLUTE 4
>> 14 RETURN_VALUE
>>>
Run Code Online (Sandbox Code Playgroud)
注意代码对象LIST_APPEND反汇编中的操作<listcomp>码.来自文档:
LIST_APPEND(I)
打电话
list.append(TOS[-i], TOS).用于实现列表推导.
现在,对于列表重复操作,如果我们考虑,我们会有一个提示:
>>> import sys
>>> sys.getsizeof([])
64
>>> 8*10
80
>>> 64 + 80
144
>>> sys.getsizeof([None]*10)
144
Run Code Online (Sandbox Code Playgroud)
所以,似乎能够准确地分配大小.看一下源代码,我们看到这正是发生的事情:
static PyObject *
list_repeat(PyListObject *a, Py_ssize_t n)
{
Py_ssize_t i, j;
Py_ssize_t size;
PyListObject *np;
PyObject **p, **items;
PyObject *elem;
if (n < 0)
n = 0;
if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n)
return PyErr_NoMemory();
size = Py_SIZE(a) * n;
if (size == 0)
return PyList_New(0);
np = (PyListObject *) PyList_New(size);
Run Code Online (Sandbox Code Playgroud)
即,这里:size = Py_SIZE(a) * n;.其余的函数只是填充数组.