Như đã lưu ý trong câu hỏi này, việc hiểu danh sách sử dụng list.append
dưới mui xe, vì vậy nó sẽ gọi phương thức thay đổi kích thước danh sách, mà tổng thể hóa.
Để chứng minh điều này với chính mình, bạn thực sự có thể sử dụng trình giải mã dis
:
>>> 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
>>>
Lưu ý LIST_APPEND
opcode trong việc tháo gỡ <listcomp>
đối tượng mã. Từ các tài liệu :
LIST_APPEND (i)
Các cuộc gọi list.append(TOS[-i], TOS)
. Được sử dụng để thực hiện việc hiểu danh sách.
Bây giờ, đối với hoạt động lặp lại danh sách, chúng tôi có một gợi ý về những gì đang xảy ra nếu chúng tôi xem xét:
>>> import sys
>>> sys.getsizeof([])
64
>>> 8*10
80
>>> 64 + 80
144
>>> sys.getsizeof([None]*10)
144
Vì vậy, nó dường như có thể phân bổ chính xác kích thước. Nhìn vào mã nguồn , chúng tôi thấy đây chính xác là những gì xảy ra:
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);
Cụ thể, ở đây : size = Py_SIZE(a) * n;
. Phần còn lại của các hàm chỉ đơn giản là điền vào mảng.
144 == sys.getsizeof([]) + 8*10)
trong đó 8 là kích thước của một con trỏ.