python list实现原理

数据如何在内存中存储?

在32位的计算机上,1个字节有8位,内存寻址最小的单位是字节。假设我们有一个int类型的值,他的内存地址从0x01开始,int类型占据4个字节,则其结束于0x13。
在这里插入图片描述

那么数据类型有什么意义呢

它确定了特定类型的数据需要申请多大的内存地址来存储,并且决定取到的二进制数该如何解释。地址存储的只有二进制数,但是对于数字和字符,同一个二进制数代表的意义是不同的。

同类型的数据在内存中是如何连续存储的?

假设将一个含有4个数字的集合(数学意义上的集合,下同)连续地存储在一起,在内存里的表现就像是他们紧挨在一起。如果第一个元素从0x10开始,那整个集合就在0x25结束。
因为类型相同,所以每个元素的偏移量也相同,公式如下:(c是元素类型的大小):
在这里插入图片描述

这就是为什么集合要从0开始。根据下标获取指定元素,只需要计算偏移量,而不用遍历整个集合。

顺序表在内存中的结构是什么?

要在内存中给集合开辟一块区域,得先确定大小;确定区域后,还需要知道当前已经占用了多少个元素,一旦溢出,就要重新申请空间。
要表达这种结构,有两种实现方式。一种是把头信息和元素串到一起,形成一个元素个数+2的表。另一种就是把头信息和元素分开放,两者之间用一个元素建立一个链接,连在一起。
在这里插入图片描述

存储表信息的单元与元素存储区以连续的方式安排在一块存储区里,两部分数据的整体形成一个完整的顺序表对象。一体式结构整体性强,易于管理。但是由于数据元素存储区域是表对象的一部分,顺序表创建后,元素存储区就固定了。
分离式结构中表对象里只保存与整个表有关的信息(即容量和元素个数),实际数据元素存放在另一个独立的元素存储区里,通过链接与基本表对象关联。
一旦表需要扩充,对于一体式结构来说,就要重新申请一块更大的空内存区域,将所有元素放入其中,再清空旧的内存区域。
对于分离式结构来说,则需要将链接地址更新一下,顺序表对象是不变的。

说到扩充,又是如何进行的呢?

采用分离式结构的顺序表,若将数据区更换为存储空间更大的区域,则可以在不改变表对象的前提下对其数据存储区进行了扩充,所有使用这个表的地方都不必修改。
扩充的策略可以说有两种。
每次扩充增加固定数目的存储位置,如每次扩充增加10个元素位置,这种策略可称为线性增长。特点:节省空间,但是扩充操作频繁,操作次数多。(就是以时间换空间,以后每次添加的元素过多就要多花时间重新扩容)
每次扩充容量加倍,如每次扩充增加一倍存储空间。特点:减少了扩充操作的执行次数,但可能会浪费空间资源。(以空间换时间,每次扩容占用的空间大了,但扩容就可以少执行些)

不同类型的数据集合在内存中是如何存储的?

当集合包含不同类型的数据时,用偏移量来定位就不可靠了,因为各自类型不同。
假设集合里有12,1.2,'ab’三个元素,他们的位置各不连续,分散在不同的地方。申请一块3个元素大小的连续内存,里面每个元素分别指向集合的三个元素。
这时的元素是外置的。
在这里插入图片描述

现在让我们来看看python中的list。
1.元素有位置下标,可以通过索引获取元素 --> 连续的存储空间,计算偏移量获取元素。
2. 元素无论如何改变,表对象都不变 --> 分离式结构,表头和元素内容分开存储。这样在更改list时,表对象始终是同一个,只是指向的地址不同
3. 元素可以是任意类型 --> 既要求连续存储,又可以存储不同类型的数据,用的是元素外置的方式,存储的只是数据地址的引用
4. 可以任意添加新元素 --> 动态扩充的策略

list底层实现

list源码如下:

typedef struct {   
	PyObject_VAR_HEAD    
	/* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */    PyObject **ob_item;
    /* ob_item contains space for 'allocated' elements.  The number    
     * currently in use is ob_size.    
     * Invariants:      
     *     0 <= ob_size <= allocated     
     *     len(list) == ob_size     
     *     ob_item == NULL implies ob_size == allocated == 0     
     * list.sort() temporarily sets allocated to -1 to detect mutations.     
     *     
     * Items must normally not be NULL, except during construction when     
     * the list is not yet visible outside the function that builds it.     */    
     Py_ssize_t allocated;
} PyListObject;

list 本质上是一个长度可变的连续数组。 其中 ob_item 是一个指针列表,里边的每一个指针都指向列表中的元素,而 allocated 则用于存储该列表目前已被分配的空间大小。
需要注意的是,allocated 和列表的实际空间大小不同,列表实际空间大小,指的是 len(list) 返回的结果,也就是上边代码中注释中的 ob_size,表示该列表总共存储了多少个元素。而在实际情况中,为了优化存储结构,避免每次增加元素都要重新分配内存,列表预分配的空间 allocated 往往会大于 ob_size。
因此 allocated 和 ob_size 的关系是:allocated >= len(list) = ob_size >= 0。
如果当前列表分配的空间已满(即 allocated == len(list)),则会向系统请求更大的内存空间,并把原来的元素全部拷贝过去。

https://www.cnblogs.com/yifeixu/p/8893823.html
http://c.biancheng.net/view/5360.html

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值