Conclusion first:

A list is an array of Pointers that hold the address of each element

      

      

Several experiments have been done in the last few days, exploring some of the things in Python LIS T. If you want to see how I came to the conclusion step by step, you can see the following

Python address changes when changing the value of a list

How are addresses assigned to python lists

Explore whether Python uses an integer buffer pool to cache a specified range of integers

      

None of this addresses my ultimate question. The list contains the values of the elements. For example, the ID of 1 is the same every time it is run, and it remains the same for countless times. It turns out that Python uses an integer buffer pool to cache a specified range of integers (see the link above). After a series of changes, the list contains characters, strings, and lists. After a bit of manipulation, it turns out that the original understanding is completely wrong.

I couldn’t sleep, so I patiently read the open source C code for the List implementation on GitHub. This time the understanding should be no problem.

a = [1.2.'a'.'dfg'[3.4]]
print(id(a))
print("Original element id:")
for i in range(len(a)):
    print(id(a[i]))

for i in a[4] :print(id(i))
Copy the code

The output

185607304

Original element ID: 8791494284320 8791494284352 34249056 42248432 185607176 8791494284384 8791494284416

My Visio flower diagram should be ok, basically can explain clearly

My conclusion: In Python, a list is a dynamic array of Pointers. Id (list) returns 185607304, the address of each element in this array. This is the only conclusion that is generally true

      

      

GitHub list implementation of open source C code

If I can’t open the url, I’ve got the code here, so delete the comments and I’m left with the following

#ifndef Py_LISTOBJECT_H
#define Py_LISTOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_LIMITED_API
typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;
#endif

PyAPI_DATA(PyTypeObject) PyList_Type;
PyAPI_DATA(PyTypeObject) PyListIter_Type;
PyAPI_DATA(PyTypeObject) PyListRevIter_Type;
PyAPI_DATA(PyTypeObject) PySortWrapper_Type;

#define PyList_Check(op) \
    PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LIST_SUBCLASS)
#define PyList_CheckExact(op) (Py_TYPE(op) == &PyList_Type)

PyAPI_FUNC(PyObject *) PyList_New(Py_ssize_t size);
PyAPI_FUNC(Py_ssize_t) PyList_Size(PyObject *);
PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, Py_ssize_t);
PyAPI_FUNC(int) PyList_SetItem(PyObject *, Py_ssize_t, PyObject *);
PyAPI_FUNC(int) PyList_Insert(PyObject *, Py_ssize_t, PyObject *);
PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyList_GetSlice(PyObject *, Py_ssize_t, Py_ssize_t);
PyAPI_FUNC(int) PyList_SetSlice(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
PyAPI_FUNC(int) PyList_Sort(PyObject *);
PyAPI_FUNC(int) PyList_Reverse(PyObject *);
PyAPI_FUNC(PyObject *) PyList_AsTuple(PyObject *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *);

PyAPI_FUNC(int) PyList_ClearFreeList(void);
PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out);
#endif

#ifndef Py_LIMITED_API
#define PyList_GET_ITEM(op, i) (((PyListObject *)(op))->ob_item[i])
#define PyList_SET_ITEM(op, i, v) (((PyListObject *)(op))->ob_item[i] = (v))
#define PyList_GET_SIZE(op)    (assert(PyList_Check(op)),Py_SIZE(op))
#define _PyList_ITEMS(op)      (((PyListObject *)(op))->ob_item)
#endif

#ifdef __cplusplus
}
#endif
#endif / *! Py_LISTOBJECT_H */
Copy the code

And then this is what really defines a list

typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;
Copy the code
  • Ob_item is an array of Pointers to list objects;

  

  • Allocated memory slots Number of allocated memory slots. Getsizeof () computes the growth pattern of the list

] (www.cnblogs.com/hellcat/p/8)…

So, I get the idea, modifying the list is just changing the position of the array of Pointers,

For example, if the string 45ijk is in memory at the beginning of 42117360, I now execute a[1] = “45ijk” to do the following

— — — — — — — — — — — — — — — — — — — — — — 2020.9.15 update — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — –

The source code for list mentioned above is as follows

typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;
Copy the code

But what is PyObject_VAR_HEAD when I look again today? PyObject_HEAD is a macro from the point of view of the code, as anyone who should be familiar with C can tell (without the semicolon ending it is correct, that must be a macro). Looking at the source code, the two properties defined in the macro are:

int ob_refcnt;        
struct _typeobject *ob_type;
Copy the code

These two properties are inherent to all Python objects:

  • Ob_refcnt: Reference count of an object, related to Python’s memory management mechanism, which implements garbage collection based on reference counts

  • Ob_type: Describes the type of a Python object.

— — — — — — — — — — — — — — — — — — — — — — 2020.9.15 update — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — –