나는이 목록을 작성 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))
그러나 결과는 나를 놀라게했습니다.
Size of l1 = 144
Size of l2 = 192
리스트 이해력으로 생성 된리스트는 메모리에서 더 큰 크기이지만, 그렇지 않으면 파이썬에서 두리스트는 동일합니다.
왜 그런 겁니까? 이것이 CPython 내부적 인 것입니까, 아니면 다른 설명입니까?
답변
을 쓸 때 [None] * 10
Python은 정확히 10 개의 객체 목록이 필요하다는 것을 알고 있으므로 정확히 할당합니다.
리스트 이해를 사용할 때, 파이썬은 그것이 얼마나 필요한지 알지 못합니다. 따라서 요소가 추가됨에 따라 목록이 점차 커집니다. 각 재 할당마다 즉시 필요한 것보다 많은 공간을 할당하므로 각 요소에 대해 재 할당 할 필요가 없습니다. 결과 목록은 필요한 것보다 다소 클 수 있습니다.
비슷한 크기로 작성된 목록을 비교할 때이 동작을 볼 수 있습니다.
>>> 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
첫 번째 방법은 필요한 것만 할당하고 두 번째 방법은 주기적으로 자라는 것을 알 수 있습니다. 이 예에서는 16 개의 요소에 충분히 할당되었으며 17 일에 다시 할당해야했습니다.
답변
이 질문 에서 언급했듯이 목록 이해력은 list.append
후드에서 사용하므로 목록 크기 조정 메소드를 호출하여 전체적으로 할당합니다.
이것을 직접 보여주기 위해 실제로 dis
dissasembler를 사용할 수 있습니다 :
>>> 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
>>>
코드 객체 LIST_APPEND
의 디스 어셈블리 에서 opcode를 확인하십시오 <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
따라서 크기 를 정확하게 할당 할 수있는 것 같습니다 . 소스 코드를 살펴보면 이것이 정확히 어떻게되는지 볼 수 있습니다.
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);
즉, 여기에 : size = Py_SIZE(a) * n;
. 나머지 함수는 단순히 배열을 채 웁니다.
답변
None은 메모리 블록이지만 미리 지정된 크기가 아닙니다. 또한 배열 요소 사이에 배열에 여분의 간격이 있습니다. 다음을 실행하여 직접 볼 수 있습니다.
for ele in l2:
print(sys.getsizeof(ele))
>>>>16
16
16
16
16
16
16
16
16
16
총 크기는 l2가 아니라 오히려 적습니다.
print(sys.getsizeof([None]))
72
그리고 이것은의 크기의 10 분의 1보다 훨씬 큽니다 l1
.
숫자는 운영 체제의 세부 사항과 운영 체제의 현재 메모리 사용량의 세부 사항에 따라 달라집니다. [없음]의 크기는 변수가 저장되도록 설정된 사용 가능한 인접 메모리보다 클 수 없으며 나중에 동적으로 더 크게 할당 된 경우 변수를 이동해야 할 수도 있습니다.