Python容器专题 - 列表(list)
1. 快速入门列表
1.1 创建列表
【eg1-1】创建列表的常见方式汇总
a = [] # 创建空列表 b = [1,2,3,4,5,'6',{7}] # 创建拥有不同数据类型成员的列表 c = list((0,1)) # 通过强制类型转换创建列表 d = ['零','一','二','三','四','五','六'][1:5] # 通过列表的切片创建子列表 e = [i for i in range(1,16,2)] # 使用嵌套的表达式建立列表,用到了列表推导式的写法 f = list(map(lambda x: x, range(1,16,2))) # 效果上与e相同 g = [[1,2,3,4,5],[5,4,3,2,1]] # 创建二维列表 print('\na=',a,'\nb=',b,'\nc=',c,'\nd=',d,'\ne=',e,'\nf=',f,'\ng=',g)
Out[]:
a= [] b= [1, 2, 3, 4, 5, '6', {7}] c= [0, 1] d= ['一', '二', '三', '四'] e= [1, 3, 5, 7, 9, 11, 13, 15] f= [1, 3, 5, 7, 9, 11, 13, 15] g= [[1, 2, 3, 4, 5], [5, 4, 3, 2, 1]]
另外,在创建列表时我们还可以用“list * num
”的表达式简写重复的元素,例如:
【eg1-2】“list * num
”的使用
[1,2] * 2
Out[2]:
[1, 2, 1, 2]
从结果中我们可以看到,“* 2”这一操作既没有升级为二维列表,也不是得到两个列表,更不是将原列表中所有的元素都X2,而仅仅是把原来列表中的元素原封不动且不改变顺序地插入了一遍。类似地,我们也可以在创建多维列表时使用该方法,如:
【eg1-3】通过“list * num
”创建多维数组
[[1,2] * 2] * 3
Out[3]:
[[1, 2, 1, 2], [1, 2, 1, 2], [1, 2, 1, 2]]
这样创建地多列表组看起来好像和普通方法列表一样,特别是使用print()函数输出时,它似乎就是普通列表。但是在对其赋值时却会有让人预料外地结果:
【eg1-4】
a = [[1] * 2] * 3 a[0][1] = 3 a
Out[4]:
[[1, 3], [1, 3], [1, 3]]
这与我们所期待的结果[[1, 3], [1, 2], [1, 2]]似乎有那么些不同——由*3创建的所有子数组全部被赋值了而不仅仅是第一个子数组[1,2]赋值成[1,3]。很好!这就是这里要提示的一个知识点。
发生上述现象的原因在于使用 *
对列表执行重复操作并不是创建副本,它只是创建现有对象的引用! *3 创建了对长度为二的同一列表的 3 个引用。 正是如此,对某一行的改变会作用于所有行。
基于以上的特性,在创建有相同子列表的列表时,先创建一个所需长度的列表,其相同子列表作为元素的部分使用*
将显得容易,比如:
【eg1-5】
a = [1,2] * 2 # [1,2, 1,2] for i in range(2): a[i] = [1,3] * 2 a
Out[5]:
[[1, 3, 1, 3], [1, 3, 1, 3], 1, 2]
1.2 列表索引与切片
【eg1-6】通过使用“[i]”实现列表的索引:
a = [1, 2, 3, 4] a[-1]
Out[5]:
4
【eg1-7】通过使用“:”运算符实现列表的切片:
a = [1, 2, 3, 4] a[0:1]
Out[6]:
[1]
1.3 列表的合并
【eg1-8】通过使用“+”运算符实现两个列表的合并:
a = [1, 2, 3, 4] b = [5, 6, 7, 8] a+b
Out[7]:
[1, 2, 3, 4, 5, 6, 7, 8]
也可以通过list.extend()
方法实现两个列表的合并,详见下一节。
1.4 元素存在性
a = ["老虎","狮子","豹子","袍子","猴子","猩猩","狗熊"] "北极熊" in a # 判断某元素是否在列表a中,结果为 False “"猴子" in a # 结果为 True "袍子" not in a # 结果为 False
1.5 统计列表
# 列表元素的统计 a = [i for i in 'ABCDEFGHIJKABCDEFGHIJK'] a.count('A') # 其结果为 2,表明该元素在列表中出现过2次
1.6 反转列表
# 反转 a = [1, 2, 3, 4, 5, 6, 7] a.reverse() # 反转列表元素,[7, 6, 5, 4, 3, 2, 1]
1.7 列表排序
# 排序 a = [ 'zl赵六', 'lh李华', 'lm李明', 'xh小红','zs张三', 'lh李华', 'ww王五', 'zl赵六', 'xh小红', 'lh李华'] print(a.sort()) # 对列表中的元素进行排序 # 结果为 [‘lh李华’, ‘lh李华’, ‘lh李华’, ‘lm李明’, ‘ww王五’, ‘xh小红’, ‘xh小红’, ‘zl赵六’, ‘zl赵六’, ‘zs张三’] b = [9,7,8,5,2,4,5,4,5] b.sort() print(b) # 结果为 [2, 4, 4, 5, 5, 5, 7, 8, 9]
注意:列表的
sort()
方法本身并不返回值,它会改变原列表元素的顺序。如果仅想返回一个排序后的列表,可以使用sorted()
语句,该语句不会改变原列而返回一个新的排序后的列表,如:
a = [9,7,8,5,2,4,5,4,5] print("a=",a) sorted(a)
1.8 拷贝列表
在Python语言中,拷贝可以分为深拷贝与浅拷贝,列表(list)自带浅拷贝方法.copy()
# 获取列表的一个浅拷贝 a = [0, 1, 2, 3, 4, 3, 2, 1] a.copy()
除此之外,你也可以使用Python的标准库copy
来实现拷贝,如:
import copy a = ["你","好"] copy.copy(a) # 返回对象a的浅拷贝 copy.deepcopy(a) # 返回对象a的深拷贝
2 列表推导式
2.1 列表推导式的概念
列表推导式,又称列表生成式。用来创建(生成)列表的表达式就是列表推导式,它也称为列表生成式。
本质上列表推导式就是一个简写的for语句。列表推导式中最先出现的与快是后面for语句中遍历时的迭代量的函数,描述了最终返回的列表的每一个元素。每个列表推导式都在for语句后跟一个表达式,然后有若干个for或者if子句。返回结果是一个根据该简写的for语句块生成的列表。
2.2 通过列表推导式将方法应用于一系列对象
以obj
表示某个对象,method()
是该对象的一个方法,其使用方法如:
result = [obj.method() for obj in mylist]
3. 列表的常用方法
3.1 列表元素的增删改以及索引
– 增 –
list.append(x) - 在列表的末尾添加一个元素。
- 相当于 a[len(a):] = [x] 。
【eg1-9】使用列表的append()方法在末尾插入新元素
a = [] for i in range(19): a.append(i) a
Out[8]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
list.insert(i, x) - 在指定的位置插入一个元素。
- 第一个参数是要插入的元素的索引,所以 a.insert(0, x) 插入列表头部, a.insert(len(a), x) 等同于 a.append(x) 。
【eg1-10】在某列表a的原第3号位置插入一个新元素
a = [0, 1, 2, 3, 4] a.insert(3, '我是新来的') a
Out[9]:
[0, 1, 2, '我是新来的', 3, 4]
【注意】:使用insert()方法进行插入时,以第一个参数为原先列表的索引值i一定是从0开始计算的。插入元素后,后面的元素先依次往后挪动出一个位置,直至原来在该位置的元素向后挪动出一个空位。最后才把新的元素放在这个唯一的空位上。
– 删 –
list.pop([i]) - 删除列表中给定位置的元素并返回它。
- 如果没有给定位置,a.pop() 将会删除并返回列表中的最后一个元素。
- 【注意】:方法签名中 i 两边的方括号表示这个参数是可选的,而不是需要你再调用的时候输入方括号,本文之后的类容中出现的所有可选区参数在表示上都将遵循该规则。
【eg1-11】
a = [0, 1, 2, 3, 4] a.pop() # pop()函数的返回值是出栈或被删除的元素
Out[10]:
4
a
Out[11]:
[0, 1, 2, 3]
【eg1-12】
a = [0, 1, 2, 3, 4] a.pop(2) a
Out[12]:
[0, 1, 3, 4]
list.remove(x) - 移除列表中第一个值为 x 的元素。
- 如果没有这样的元素,则抛出 ValueError 异常。
【eg1-13】
a = [0, 1, 2, 3, 4, 3, 2, 1] a.remove(3) a
Out[13]:
[0, 1, 2, 4, 3, 2, 1]
list.clear() - 删除列表中所有的元素。
- 相当于 del a[:] 。
【eg1-14】
a = [0, 1, 2, 3, 4, 3, 2, 1] a.clear() a
Out[14]:
[13]
– 改 –
list.extend(iterable) - 使用可迭代对象中的所有元素来扩展列表。
- 相当于 a[len(a):] = iterable 。
我们上面已经介绍果了通过列表的加运算来实现列表的合并。 应该指出的是加运算符只能合并两个列表,而这里的extend()方法却适用于所有课迭代对象。
【eg1-15】使用list的extend()方法合并另一个列表对象:
a = [0, 1, 2, 3, 4]; b = ['5','6','7'] a.extend(b) a
Out[15]:
[0, 1, 2, 3, 4, '5', '6', '7']
【eg1-16】使用list的extend()方法合并一个集合对象:
a = [0, 1, 2, 3, 4]; b = {'5','6','7'} a.extend(b) a
Out[16]:
[0, 1, 2, 3, 4, '5', '7', '6']
【注意】:使用extend()方法合并列表与合并集合有什么不同呢?细心观察上例的同学应该发现一个问题——合并后集合部分的顺序改变了!这里的原因是集合是无序的而列表是有序的。但需要指出的是这并不代表在合并时候合并是随机进行的!上面的代码无论执行多少次它将总是和你第一次所看到的执行结果相同。
【eg1-17】使用list的extend()方法合并一个字典对象:
a = [0, 1, 2, 3, 4]; b = {'5':'五','6':'六','7':'气'} a.extend(b) a
Out[17]:
[0, 1, 2, 3, 4, '5', '6', '7']
在这个例子中我们可以看到,合并时并不是合并某个键值对,而是依据字典原来顺序的所有键构成的列表。
– 反查索引 –
list.index(x[, start[, end]]) - 返回列表中第一个值为 x 的元素的从零开始的索引。
- 在列表的“索引与切片”部分我们时通过由列表的索引值获取指定的元素或者子列表。然而这里与以上过程恰好时相反的,我们的目的是查询列表的索引
- 如果没有这样的元素将会抛出 ValueError 异常。
- 可选参数 start 和 end 是切片符号,用于将搜索限制为列表的特定子序列。返回的索引是相对于整个序列的开始计算的,而不是 start 参数。
【eg1-18】获取字符串’ABCDEFGHIJKABCDEFGHIJK’中第一个字母’G’的索引
a = [i for i in 'ABCDEFGHIJKABCDEFGHIJK'] # 一一映射字符串为列表 a.index('G') # 使用列表的index()方法查询'G'的索引
Out[18]:
6
【提醒】:第一个索引值是0。从第一个字母’A’开始,第1个字母’G’的索引是6,因此返回值是6。
【eg1-19】在字符串’ABCDEFGHIJKABCDEFGHIJK’中从第位置2开始寻找下一个字母’G’的索引
a = [i for i in 'ABCDEFGHIJKABCDEFGHIJK'] a.index('G', 7)
Out[19]:
17
【提示】:从第一个字母’A’开始,第1个字母’G’的索引是6,第2个字母’G’的索引是17。索引为7的字母是第1个’H’,它已经在第1个G后了,接下的的第1个’G’字母也就是第二个’G’字母,其索引值为17。
3.2 列表的拷贝方法
list.copy() - 返回列表的一个浅拷贝。
- 相当于 a[:] 。
【eg1-20】
a = [0, 1, 2, 3, 4, 3, 2, 1] a.copy()
Out[20]:
[0, 1, 2, 3, 4, 3, 2, 1]
3.3 列表元素的统计
list.count(x) - 返回元素 x 在列表中出现的次数。
【eg1-21】
a = [i for i in 'ABCDEFGHIJKABCDEFGHIJK'] a.count('A')
Out[21]:
2
3.4 列表元素的排序和反转
list.sort(*, key=None, reverse=False) - 对列表中的元素进行排序
- 其中参数可用于自定义排序,与sorted()函数类似。
- 若reverse=True,则将在reverse=False的基础上将结果反转后返回。
【eg1-22】
a = [ 'zl赵六', 'lh李华', 'lm李明', 'xh小红','zs张三', 'lh李华', 'ww王五', 'zl赵六', 'xh小红', 'lh李华'] a.sort() a
Out[22]:
[‘lh李华’, ‘lh李华’, ‘lh李华’, ‘lm李明’, ‘ww王五’, ‘xh小红’, ‘xh小红’, ‘zl赵六’, ‘zl赵六’, ‘zs张三’]
list.reverse() - 反转列表中的元素。
【eg1-23】
a = [1, 2, 3, 4, 5, 6, 7] a.reverse() a
Out[23]:
[7, 6, 5, 4, 3, 2, 1]
3.5 交换二维列表的行和列(转置)
【eg1-24】当一个二维列表的每一个子列表拥有相同的长度时,可以视作一个矩阵。可以通过如下方法交换其行列:
# 定义一个矩阵类 class Matrix(object): def __init__(self,alist): self.__alist = alist def __str__(self): return str(self.__alist) def T(self): """矩阵的转置""" self.__alist = [[row[i] for row in self.__alist] for i in range(len(self.__alist[0]))] return self.__alist
下面传入一个列表来测试一下:
# 先定义一个列表 alist = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], ] # 将列表传入该矩阵类 matrix = Matrix(alist) # 转字符串查看 str(matrix)
Out[24]:
'[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]'
接着测试一下行列互换
matrix.T()
Out[25]:
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
5. 使用zip()函数对列表构建一一对应的可迭代对象
zip()函数接受任意多个可迭代对象,返回的是一个可以迭代的zip对象,如果在jupyter notebook中查看,则显示的内容形式如‘<zip at 0x13fd1e99f40>’。
先来看一个需求吧。从李华的一次期末考试说起吧:
# 考试科目 labels = ['语文','数学','英语','物理','化学','生物','政治'] # 横坐标(表示横轴位置、无含义) x = [1,2,3,4,5,6,7] # 纵坐标(考试得分) y = [120,55,129,59,89,73,97]
考虑绘制一个bar图来表示李华各科的成绩以可视化呈现李华在不同科目上的表现,我们需要知道每一个科目在图上的位置。这意味着若当我们自己实现这样一个绘图库时我们需要将labels、x、 y三者的元素一一对应起来。如果没有zip()函数,我们可能需要这样做:
【eg1-25】不使用zip()函数时的实现方法
alist = [] for i in range(len(labels)): alist.append((x[i],y[i],labels[i])) alist
Out[26]:
[(1, 120, '语文'), (2, 55, '数学'), (3, 129, '英语'), (4, 59, '物理'), (5, 89, '化学'), (6, 73, '生物'), (7, 97, '政治')]
然而有了zip()函数以后,同样的功能实现起来方便很多:
【eg1-26】使用zip函数实现多列表元素一一对应为新列表的子元组:
list(zip(x,y,labels))
Out[27]:
[(1, 120, '语文'), (2, 55, '数学'), (3, 129, '英语'), (4, 59, '物理'), (5, 89, '化学'), (6, 73, '生物'), (7, 97, '政治')]
6. 使用del语句删除列表或从列表按照给定的索引删除元素
pop()方法从原列表删除指定位置的元素并返回被删除的元素值。而del语句广泛用于从内存中将某个对象删除,它与pop()方法有诸多不同:
- pop()方法有返回值,del()却不存在返回值。
- python中一切皆对象。每一个对象再他们创建后都将被分配一个内存空间。列表(list)是一种复合数据类型,一个列表本身是一个对象,而列表内的数据亦如此。因此使用del语句不仅能够从内存中删除列表内的某个元素,还能删除整个列表:
【eg1-27】使用del语句删除列表中某个数据:
a = [1, 2, 3, 4, 5, 6, 7] del(a[2]) a
Out[28]:
[1, 2, 4, 5, 6, 7]
【eg1-28】使用del语句删除整个列表:
a = [1, 2, 3, 4, 5, 6, 7] del(a) a
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-252-6553c0a6fea1> in <module> 1 a = [1, 2, 3, 4, 5, 6, 7] 2 del(a) ----> 3 a NameError: name 'a' is not defined
可以看到,a这个变量已经不存在了。因为其作为一个对象已经被del语句从内存中彻底地删除掉了。
有时如果你想使用del删除某个列表所有的数据对象,可以这样写:
【eg1-29】
a = [1, 2, 3, 4, 5, 6, 7] del a[:] a
Out[29]:
[]
7. python列表去重的常用方法
7.1 通过集合(set)对列表去重
在python中列表和集合的区别在于列表中的元素是允许重复的而集合却不行。因此在日常编写代码过程中我们常常使用集合来实现列表中元素的去重:
【eg1-30】对列表中的元素进行去重:
alist = [9,1,1,2,2,1,3,0,5] set(alist) # 由于我们值关心其中的元素是否有重复的就不转列表了
Out[29]:
{0, 1, 2, 3, 5, 9}
应用举例:统计列表中所有元素的出现次数
names = ['小明','李华','张三','小明','小明','王五','李四','赵六','王五','张三','李四'] counter = {} for i in set(names): counter[i]=names.count(i) counter
Out[30]:
{‘张三’: 2, ‘王五’: 2, ‘李四’: 2, ‘赵六’: 1, ‘李华’: 1, ‘小明’: 3}
【注】:python中自带了一个集合模块collection
提供了获取可迭代对象中每个元素出现次数的方法。
方法3 - 通过强制转化为字典(dict)对列表去重
与方法1思路基本一致,因为在字典数据结构中,键是不能有重复的。
alist = [9,1,1,2,2,1,3,0,5] list({}.fromkeys(alist).keys())
Out[31]:
[9, 1, 2, 3, 0, 5]
7.2 通过列表推导去重
alist = [9,1,1,2,2,1,3,0,5] a = [] [a.append(i) for i in alist if not i in a]
Out[32]:
[9, 1, 2, 3, 0, 5]
8. 列表求交集、并集、差集
8.1 求并交
a = [0,1,4,6,6,5,1] b = [3,2,6,7,0,9,13,1] [i for i in a if i in b]
Out[33]:
[0, 1, 6, 6, 1]
8.2 求并集
a = [0,1,4,6,6,5,1] b = [3,2,6,7,0,9,13,1] list(set(a) | set(b))
Out[34]:
[0, 1, 2, 3, 4, 5, 6, 7, 9, 13]
8.3 求差集
[友情提示]:数学上差集指在前一个集合排除后一个集合中存在的元素,且两个集合都是无重复元素的。由于Python列表中是允许存在重复元素的,因此我们定义两个列表的差集是它们对应的集合的差集。
A = [0,1,4,6,6,5,1] B = [3,2,6,7,0,9,13,1] setB = list(set(B)) [i for i in set(A) if i not in setB]
Out[34]:
[4, 5]