Wie in dieser Frage erwähnt, wird das Listenverständnis list.append
unter der Haube verwendet, sodass die Methode zur Größenänderung der Liste aufgerufen wird, die insgesamt zugeordnet wird.
Um sich dies zu demonstrieren, können Sie den dis
Dissasembler tatsächlich verwenden :
>>> 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
>>>
Beachten Sie den LIST_APPEND
Opcode bei der Demontage des <listcomp>
Codeobjekts. Aus den Dokumenten :
LIST_APPEND (i)
Anrufe list.append(TOS[-i], TOS)
. Wird verwendet, um Listenverständnisse zu implementieren.
Für die Listenwiederholungsoperation haben wir nun einen Hinweis darauf, was los ist, wenn wir Folgendes berücksichtigen:
>>> import sys
>>> sys.getsizeof([])
64
>>> 8*10
80
>>> 64 + 80
144
>>> sys.getsizeof([None]*10)
144
Es scheint also in der Lage zu sein , die Größe genau zuzuweisen. Wenn wir uns den Quellcode ansehen, sehen wir, dass genau dies passiert:
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);
Nämlich hier : size = Py_SIZE(a) * n;
. Der Rest der Funktionen füllt einfach das Array.
144 == sys.getsizeof([]) + 8*10)
8 die Größe eines Zeigers ist.