Why do two identical lists have a different memory footprint?

前端 未结 3 818
一整个雨季
一整个雨季 2020-11-28 06:15

I created two lists l1 and l2, but each one with a different creation method:

import sys

l1 = [None] * 10
l2 = [None for _ in rang         


        
3条回答
  •  佛祖请我去吃肉
    2020-11-28 06:31

    As noted in this question the list-comprehension uses list.append under the hood, so it will call the list-resize method, which overallocates.

    To demonstrate this to yourself, you can actually use the dis dissasembler:

    >>> code = compile('[x for x in iterable]', '', 'eval')
    >>> import dis
    >>> dis.dis(code)
      1           0 LOAD_CONST               0 ( at 0x10560b810, file "", line 1>)
                  2 LOAD_CONST               1 ('')
                  4 MAKE_FUNCTION            0
                  6 LOAD_NAME                0 (iterable)
                  8 GET_ITER
                 10 CALL_FUNCTION            1
                 12 RETURN_VALUE
    
    Disassembly of  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
    >>>
    

    Notice the LIST_APPEND opcode in the disassembly of the code object. From the docs:

    LIST_APPEND(i)

    Calls list.append(TOS[-i], TOS). Used to implement list comprehensions.

    Now, for the list-repetition operation, we have a hint about what is going on if we consider:

    >>> import sys
    >>> sys.getsizeof([])
    64
    >>> 8*10
    80
    >>> 64 + 80
    144
    >>> sys.getsizeof([None]*10)
    144
    

    So, it seems to be able to exactly allocate the size. Looking at the source code, we see this is exactly what happens:

    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);
    

    Namely, here: size = Py_SIZE(a) * n;. The rest of the functions simply fills the array.

提交回复
热议问题