python基础数据类型之List

136 阅读3分钟

List为什么可以存储不同类型的数据?

传统意义上的数组的时候要指明数据类型, 意味着数组里的每个数据类型是一致的, 并且指定数组长度,这样就可以根据索引实现O(1)复杂度的高效查询, 如:

image.png

因为每个数据的大小都是一致的, 所以随机读取某个数据的时候直接读取从 "首地址+索引*数据大小" 到 "首地址+(索引+1)*数据大小" 的数据, 如此就可以实现随机读取,效率是O(1)

但List可以存储不同的数据类型, 每个数据类型占用的内存大小是不同的, 如此这样就无法实现传统数组的内存读写, 为了解决这个问题, List存储的是数据在内存中的地址, 比如“数据1”先存在于内存当中,然后可以通过一个唯一的内存地址找到这个数据(这其中可能会更复杂一些,比如存储的地址可能还是个间接地址,可以先不考虑, 总之可以通过这个地址找到“数据1”), 所以List开辟的线性内存空间里每个数据单位都是内存地址, 内存地址可以占用一个固定长度的内存空间, 这样就可以实现随机的数据读写了

这种设计也是有其优缺点的 优点: 使用极其方便,不用在意list中的某个元素的数据类型, 给开发提供了极大的遍历 缺点: 效率会比传统的低,因为首先读取的是地址,还得通过这个地址去读取实际数据, 不过Python中大多数数据都会有这个问题,带来便利的同时也牺牲了一些效率

List的扩容

使用List的时候不用像传统数组那样要提前设置大小也就是数据个数,使用的时候很方便,但是基于计算机存储数据的原理,那么所有的数据都要提前跟系统要一个固定的长度,如果长度不固定,操作系统也无法分配内存了, 因为所有地址都可能被用,想想就感觉挺可怕的, 所以list申请的一定也是一个固定长度的内存空间, 但为啥能扩容呢? 因为当声明一个list的时候如: arr = list() 此时还不会开辟实际存储数据的内存空间,当arr.append(1) 的时候才会真正的开辟存储数据的线性内存空间,也就是数组,但不会只开辟长度为1的内存空间而是开辟4个, 当添加第5个的时候才会进行扩容,需要注意的是不同版本的python解释器扩容机制会有不同, 主要就是预申请策略,主要就是提前申请更大的内存空间,如果发现空间不够了就扩容

image.png

扩容对效率有影响么?

有的, 因为存储数据的部分必须为线性空间, 在原本的空间基础上扩容的时候发现后面有其他数据占用了,就要重新申请空间, 并且把数据重新复制

了解了扩容机制有啥用?

也有的, 比如预先知道数组大小可以一次性申请内存空间 如 arr = [None] * size 或列表推到 arr = [None for _ in range(size)] 等,避免在循环中使用append()在这种方式添加,一定程度上提升一些效率

List会缩容么?

会的, 为了节约空间,当实际使用空间远远小于实际占用空间的时候会缩容, 跟扩容原理差不多...