最终总结 python中 list 到底是怎么实现的,内存里面是怎么存放的
结论放前面 :
list 就是个指针数组,指针数组里面对应存放了每个元素所在的地址
前几天做了好几个实验,探究了python lis t的一些东西,想看我怎么一步步得出了结论的可以看看下面的
探究 python 是不是用了一个整数缓冲池缓存了指定范围的整数
这几篇都没解决我的终极问题。list 在内存里面是怎么存放的,原来只测试了整数以为是顺序挨个挨个存的(当时id是连着的),列表放的就是元素的值,每次运行比如说 1 的 id 都是一样的,运行无数次都不变,最后才搞清楚 python用了一个整数缓冲池缓存了指定范围的整数
(详见上面链接),后面经过一系列的改变值,列表里面有字符,有字符串,有列表,一顿操作之后发现,原来的的理解就是错的离谱。
没弄明白我睡不着啊,所以又耐着性子把 GitHub上的 list 实现的开源 c 代码看了一遍。这回的理解应该是没问题了。
a = [1,2,'a','dfg',[3,4]]
print(id(a))
print("原始的各元素id:")
for i in range(len(a)):
print(id(a[i]))
for i in a[4]:
print(id(i))
输出
185607304
原始的各元素id:
8791494284320
8791494284352
34249056
42248432
185607176
8791494284384
8791494284416
我Visio 花的图应该还可以,基本能解释清楚了
我的得出的结论:在Python中,列表是一个动态的指针数组,id(list)返回的是这个指针数组所在的地址示例里面的 185607304,这个指针数组里面对应存放了每个元素所在的地址,原来的列表是顺序存储的这个结论也还是对的,数组就是顺序存储的嘛。也只有这个结论是通用正确的
打不开网址的我把代码拿过来了,删掉注释就剩下下面的
#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 */
然后真正定义 list 的就这么点
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
- ob_item是指向列表对象的指针数组;
- allocated是申请内存的槽的个数。用于对指针数组扩内存 这儿的探究见 一、通过getsizeof()计算列表的增长模式
那我已经懂了,修改 list 元素就只是改变指针数组的对应位置值,
比如,假设 字符串45ijk
存在内存 42117360 开始的一段内存里面,我现在执行 a[1] = "45ijk"
,就是做下面操作
----------------------2020.9.15更新------------------------------------
上面提到 list 的源码如下
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
但是今天我再看的时候发现 PyObject_VAR_HEAD
是个啥呢?问题儿童上线 , 从代码来看PyObject_HEAD
是一个宏,这个应该熟悉 c 的人都能看出来(没分号结尾又是正确的,那肯定是个宏)。看了下源码,宏中定义的两个属性分别是:
int ob_refcnt;
struct _typeobject *ob_type;
这两个属性是所有Python对象固有的:
-
ob_refcnt:对象的引用计数,与Python的内存管理机制有关,它实现了基于引用计数的垃圾收集机制
-
ob_type:用于描述Python对象的类型信息。
----------------------2020.9.15更新------------------------------------