Como se señaló en esta pregunta, la comprensión de la lista se utiliza list.append
debajo del capó, por lo que llamará al método de cambio de tamaño de la lista, que se sobreasigna.
Para demostrarte esto a ti mismo, puedes usar el dis
desensamblador:
>>> 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
>>>
Observe el LIST_APPEND
código de operación en el desmontaje del <listcomp>
objeto de código. De los documentos :
LIST_APPEND (i)
Llamadas list.append(TOS[-i], TOS)
. Se usa para implementar listas de comprensión.
Ahora, para la operación de repetición de lista, tenemos una pista sobre lo que está sucediendo si consideramos:
>>> import sys
>>> sys.getsizeof([])
64
>>> 8*10
80
>>> 64 + 80
144
>>> sys.getsizeof([None]*10)
144
Entonces, parece ser capaz de asignar exactamente el tamaño. Mirando el código fuente , vemos que esto es exactamente lo que sucede:
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);
Es decir, aquí: size = Py_SIZE(a) * n;
. El resto de las funciones simplemente llena la matriz.
144 == sys.getsizeof([]) + 8*10)
donde 8 es el tamaño de un puntero.