列表是可变的
1.1 什么是列表
一个排列整齐的队伍,Python采用顺序表实现
列表内的个体称作元素,由若干元素组成
列表元素可以是任意对象(数字、字符串、对象、列表等)
列表内元素有顺序,可以使用索引
线性的数据结构
使用 [ ] 表示
列表是可变的
1.2 列表的构造
ls1 = []
ls2 = list()
ls3 = [2, 'ab', [3, 'abc'], (5, 30, 50)] #列表是一个容器,元素可以是其它类型
ls4 = list(range(5)) #非常常用的构造方式,将一个可迭代对象转换为一个列表
1.3 索引
索引,也叫下标
正索引:从左至右,从0开始,为列表中每一个元素编号
如果列表有元素,索引范围[0, 长度-1]
负索引:从右至左,从-1开始
如果列表有元素,索引范围[-长度, -1]
正、负索引不可以超界,否则引发异常IndexError
为了理解方便,可以认为列表是从左至右排列的,左边是头部,右边是尾部,左边是下界,右边是上界
列表通过索引访问,list[index] ,index就是索引,使用中括号访问使用索引定位访问元素的时间复杂度为O(1),这是最快的方式,是列表最好的使用方式.
1.3.1 正索引和负索引之间的关系
正索引=长度+负索引
1.4 查询
1.4.1 index
1.4.1.1 通过值来查找索引号,返回的是第一个索引号
1.4.1.2 通过索引号来查找对应的值
1.4.2 count
返回列表中匹配value的次数
1.4.3 index和count的效率问题
index和count返回列表中的元素的时候,这2个效率都很低下,能不用则不用
index如果列表中较少,刚好第一个取到的值就是需要的这种很高,但是这种情况是很难遇到的,大多是时候都是要去一个个查找
count是傻乎乎的遍历整个列表去查找,所以效率也很低
1.4.4 返回列表中元素的个数
函数len()这个来实现,这个效率极高
我们就想象,一个箱子里面有1000个球,难道真的去数它么?肯定是我们放一个就记录这里面有1个球了,这样到最后,我们就可以直接说出这个箱子里面有多少个球了
1.5 修改
索引定位元素.然后修改。注意索引不能超界
所谓修改,就是先找到这个元素,然后再给它赋值,说白了就是覆盖
1.6 增加
例如列表 [1, 2, 3, 4, 5]在前面增加一个元素,就是把原来的那个元素的位置占用,并且往后移动
记住:
占用原来的元素的位置,且原来的元素往后挪动
1.6.1 增加单个元素
append
列表尾部追加元素,返回None
返回None就意味着没有新的列表产生,就地修改
append的效率不一定高,虽然列表是一个容器,但是有容量,不够了就会扩容,扩容耗时,如果内存连续空间不够,会涉及到内存回收机制,这样就会造成效率问题
insert
在指定的索引index处插入元素object
返回None就意味着没有新的列表产生,就地修改
表示的是,原来站住0号索引的是1,现在我要在0号索引插入一个不同的值,那么其余的值就都需要往后挪动
1.6.2 增加多个元素
extend(iteratable) -> None (尾部追加,效率还是很高的)
将可迭代对象的元素追加进来,返回None
就地修改,本列表自身扩展
+ -> list
连接操作,将两个列表连接起来,产生新的列表,原列表不变
本质上调用的是魔术方法__add__()方法
* -> list
重复操作,将本列表元素重复n次,返回新的列表
1.7 内存模型介绍
为什么这里会全是500?
1. 列表[1]对象放在堆里面
2. a = [x] * 3 先算等式右边的,其实就是从新开辟了一段内存x,保存了[1]的被内存地址,*3就是复制3份生成了一个全新的列表
3. 这个全新的列表就是新的内存空间保存了指向[1]的内存地址,
4. a[1][0] = 500 就是a你有1号元素吗?a说有,又因为1号元素是一个列表,接着就找到这个列表的0号元素并改为500,所以我们在界面显示的时候全是[[500], [500], [500]]
在Python中一切皆对象,而对象都是引用类型,可以理解为一个地址指针指向这个对象.
但是,字面常量字符串、数值等表现却不像引用类型,暂时可以称为简单类型.
而列表、元组、字典,包括以后学习的类和实例都可以认为是引用类型。你可以认为简单类型直接存在列表中,而引入类型只是把引用地址存在了列表中
1.8 删除
1.8.1 remove
remove(value) -> None #返回的是None,就地修改,不会生成新的列表,默认是从左往右
remove的效率很低,因为它要遍历整个列表
1.8.2 pop([index])
不指定索引index,就从列表尾部弹出一个元素
指定索引index,就从索引处弹出一个元素
这个效率的高低取决于怎么用?
例如: 把首部弹走,以后的所有数据都要挪动
如果是从尾部弹走,效率很高(pop的过程是利用索引找到元素的)默认的就是从-1开始
1.8.3 clear() -> None
清除列表所有元素,剩下一个空列表
1.9 反转 reverse() -> None
将列表元素反转,返回None
就地修改
这个方法最好不用,可以倒着读取,都不要反转
例如: x = [0,1,2,3,4]
用reverse这种方法代价是非常大的,列表里面的元素越多,代价就越大
可以用别的方法来实现,但是不要用reverse这种方法来实现
第1种方法:
第2种方法:
注意边界 range是前包后不包
错误的写法
正确的写法(负索引思维)
第3种方法: 内建函数reversed()这种就是将一个序列倒着读,效率很高
注意: reversed()这个函数,要是"序列",如果不是有序的序列是不能用这个函数的
1.10 排序
sort(key=None, reverse=False) -> None (这个函数尽量不要用,因为它会把原有的列表修改掉了)
对列表元素进行排序,就地修改,默认升序
reverse为True,反转,降序
key一个函数,指定key如何排序,lst.sort(key=function)
可以用sorted来替代
sorted默认是升序
把上面的改为降序
1.11 列表复制
1.11.1 浅拷贝
第一个print(a ==b)可以很好理解
但是第二个print(a == c),a和c打印出来的结果是一模一样的,要如何理解?
其实,也是跟内存的模型是有关的
a 指向列表
b 是a的副本,由于是浅拷贝,所以,b拷贝的其实是a指向的列表的内存地址,所以a[1][1]这一步根本就没有什么实际意义
最终a和b还是相等的
shadow copy
影子拷贝,也叫浅拷贝.遇到引用类型数据,仅仅复制一个引用而已
deep copy
深拷贝,往往会递归复制一定深度
一般情况下,大多数语言提供的默认复制行为都是浅拷贝
import copy
a = [1, [2, 3], 4]
b = copy.deepcopy(a)
print(a == b)
a[1][1] = 100
print(a == b)
# 还相等吗?
深拷贝复制的和浅拷贝是不一样的,深拷贝会把拷贝的对象都复制成一个独立的副本,这样就有了单独的内存地址,所以它们是不相等的
标签:10,None,index,元素,列表,索引,拷贝
From: https://www.cnblogs.com/yufc/p/17385961.html