最终总结 python中 list 到底是怎么实现的,内存里面是怎么存放的

发表时间:2021-02-23

原文地址:https://blog.csdn.net/qq_43657442/article/details/108431857?spm=1001.2014.3001.5502

作者主页:https://blog.csdn.net/qq_43657442


结论放前面 :

list 就是个指针数组,指针数组里面对应存放了每个元素所在的地址

前几天做了好几个实验,探究了python lis t的一些东西,想看我怎么一步步得出了结论的可以看看下面的

python修改列表的值时的地址变化,一个蛮有意思的小实验

python 列表的 元素id 探寻,地址到底是怎么分配的

探究 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,这个 指针数组里面对应存放了每个元素所在的地址 ,原来的列表是顺序存储的这个结论也还是对的,数组就是顺序存储的嘛。也只有这个结论是通用正确的

GitHub上的 list 实现的开源 c 代码

打不开网址的我把代码拿过来了,删掉注释就剩下下面的

#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" ,就是做下面操作

在这里插入图片描述

文章来源互联网,如有侵权,请联系管理员删除。邮箱:417803890@qq.com / QQ:417803890

微配音

Python Free

邮箱:417803890@qq.com
QQ:417803890

皖ICP备19001818号
© 2019 copyright www.pythonf.cn - All rights reserved

微信扫一扫关注公众号:

联系方式

Python Free