Полная картина того, что происходит , основанная на других ответах и комментариях (особенно ответ ShadowRanger , который также объясняет , почему так сделано).
Разборка показывает, что BUILD_LIST_UNPACK
используется:
>>> import dis
>>> dis.dis('[*a]')
1 0 LOAD_NAME 0 (a)
2 BUILD_LIST_UNPACK 1
4 RETURN_VALUE
Это обработано в ceval.c
, который создает пустой список и расширяет его (с помощью a
) :
case TARGET(BUILD_LIST_UNPACK): {
...
PyObject *sum = PyList_New(0);
...
none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));
_PyList_Extend
использует list_extend
:
_PyList_Extend(PyListObject *self, PyObject *iterable)
{
return list_extend(self, iterable);
}
Который вызывает list_resize
с суммой размеров :
list_extend(PyListObject *self, PyObject *iterable)
...
n = PySequence_Fast_GET_SIZE(iterable);
...
m = Py_SIZE(self);
...
if (list_resize(self, m + n) < 0) {
И это перераспределяет следующим образом:
list_resize(PyListObject *self, Py_ssize_t newsize)
{
...
new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
Давайте проверим это. Вычислите ожидаемое количество пятен с помощью приведенной выше формулы и рассчитайте ожидаемый размер байта, умножив его на 8 (как я использую здесь 64-битный Python) и добавив размер байта пустого списка (то есть константу объекта списка накладные расходы):
from sys import getsizeof
for n in range(13):
a = [None] * n
expected_spots = n + (n >> 3) + (3 if n < 9 else 6)
expected_bytesize = getsizeof([]) + expected_spots * 8
real_bytesize = getsizeof([*a])
print(n,
expected_bytesize,
real_bytesize,
real_bytesize == expected_bytesize)
Вывод:
0 80 56 False
1 88 88 True
2 96 96 True
3 104 104 True
4 112 112 True
5 120 120 True
6 128 128 True
7 136 136 True
8 152 152 True
9 184 184 True
10 192 192 True
11 200 200 True
12 208 208 True
Совпадения, кроме n = 0
, которые list_extend
на самом деле ярлыки , так что на самом деле это тоже совпадает:
if (n == 0) {
...
Py_RETURN_NONE;
}
...
if (list_resize(self, m + n) < 0) {