3.2 列表
列表是包含若干元素的有序连续内存空间。在形式上,列表的所有元素放在一对方括号中,相邻元素之间使用逗号分隔。在Python中,同一个列表中元素的数据类型可以各不相同,可以同时包含整数、实数、字符串等基本类型的元素,也可以包含列表、元组、字典、集合、函数以及其他任意对象。如果只有一对方括号而没有任何元素则表示空列表。下面几个都是合法的列表对象。
[10, 20, 30, 40] ['crunchy frog', 'ram bladder', 'lark vomit'] ['spam', 2.0, 5, [10, 20]] [['file1', 200,7], ['file2', 260,9]] [{3}, {5:6}, (1, 2, 3)]
3.2.1 列表创建与删除
使用“=”直接将一个列表常量赋值给变量即可创建列表对象。
> > > a_list = ['a', 'b', 'c', 'd', 'e'] > > > a_list = [] #创建空列表
也可以使用list()函数把元组、range对象、字符串、字典、集合或其他可迭代对象转换为列表。
> > > list((3, 5, 7, 9, 11)) #将元组转换为列表 [3, 5, 7, 9, 11] > > > list(range(1, 10, 2)) #将range对象转换为列表 [1, 3, 5, 7, 9] > > > list('hello world') #将字符串转换为列表 ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] > > > list({3, 7, 5}) #将集合转换为列表,集合中的元素是无序的 [3, 5, 7] > > > list({'a':3, 'b':9, 'c':78}) #将字典的“键”转换为列表 ['a', 'b', 'c'] > > > list({'a':3, 'b':9, 'c':78}.items()) #将字典的元素转换为列表 [('a', 3), ('b', 9), ('c', 78)] > > > x = list() #创建空列表
当一个列表不再使用时,可以使用del命令将其删除。
> > > x = [1, 2, 3] > > > del x #删除列表对象 > > > x #对象删除后无法再访问,抛出异常 NameError: name 'x' is not defined
3.2.2 列表元素访问
列表属于有序序列,可以使用整数作为下标来随机访问其中任意位置上的元素,其中0表示第1个元素,1表示第2个元素,2表示第3个元素,以此类推;列表还支持使用负整数作为下标,其中-1表示最后1个元素,-2表示倒数第2个元素,-3表示倒数第3个元素,以此类推。以列表['P', 'y', 't', 'h', 'o', 'n']为例,图3-2显示了每个元素的正向索引和反向索引。
> > > x = list('Python') #把字符串转换为列表 > > > x ['P', 'y', 't', 'h', 'o', 'n'] > > > x[1] #下标为0的元素,第一个元素 'y' > > > x[-3] #下标为-1的元素,最后一个元素 'h'
图3-2 双向索引示意图
3.2.3 列表常用方法
列表对象常用的方法如表3-1所示。
表3-1 列表对象常用方法
(1)append()、insert()、extend()
append()用于向列表尾部追加一个元素,insert()用于向列表任意指定位置插入一个元素,extend()用于将另一个列表中的所有元素追加至当前列表的尾部。
> > > x = [1, 2, 3] > > > x.append(4) #在尾部追加元素 > > > x.insert(0, 0) #在指定位置插入元素 > > > x.extend([5, 6, 7]) #在尾部追加多个元素 > > > x [0, 1, 2, 3, 4, 5, 6, 7]
(2)pop()、remove()
pop()用于删除并返回指定位置(默认是最后一个)上的元素,如果指定的位置不是合法的索引则抛出异常;remove()用于删除列表中第一个值与指定值相等的元素,如果列表中不存在该元素则抛出异常。另外,还可以使用del命令删除列表中指定位置的元素。
> > > x = [1, 2, 3, 4, 5, 6, 7] > > > x.pop() #弹出并返回尾部元素 7 > > > x.pop(0) #弹出并返回指定位置的元素 1 > > > x = [1, 2, 1, 1, 2] > > > x.remove(2) #删除第一个值为2的元素 > > > del x[3] #删除指定位置上的元素 > > > x [1, 1, 1]
注意,由于列表具有内存自动收缩和扩张功能,在列表中间位置插入或删除元素时,会涉及该位置之后的元素后移或前移,不仅效率较低,而且该位置后面所有元素在列表中的索引也会发生变化。
(3)count()、index()
列表方法count()用于返回列表中指定元素出现的次数;index()用于返回指定元素在列表中首次出现的位置,如果该元素不在列表中则抛出异常。
> > > x = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] > > > x.count(3) #元素3在列表x中的出现次数 3 > > > x.index(2) #元素2在列表x中首次出现的索引 1 > > > x.index(5) #列表x中没有5,抛出异常 ValueError: 5 is not in list
(4)sort()、reverse()
sort()方法用于按照指定的规则对列表中所有元素进行原地排序;reverse()方法用于将列表所有元素原地翻转,也就是第一个元素和倒数第一个元素交换位置,第二个元素和倒数第二个元素交换位置,以此类推。
> > > x = list(range(11)) #包含11个整数的列表 > > > import random > > > random.shuffle(x) #把列表x中的元素随机乱序 > > > x [6, 0, 1, 7, 4, 3, 2, 8, 5, 10, 9] > > > x.sort(key=lambda item:len(str(item)), reverse=True) #按转换成字符串以后的长度 #降序排列 > > > x [10, 6, 0, 1, 7, 4, 3, 2, 8, 5, 9] > > > x.sort(key=str) #按转换为字符串后的大小 #升序排序 > > > x [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9] > > > x.sort() #按默认规则排序 > > > x [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] > > > x.reverse() #把所有元素翻转或逆序 > > > x [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
3.2.4 列表对象支持的运算符
加法运算符+可以连接两个列表,得到一个新列表。
> > > x = [1, 2, 3] > > > x = x + [4] > > > x [1, 2, 3, 4]
乘法运算符*可以用于列表和整数相乘,表示序列重复,返回新列表。
> > > x = [1, 2, 3, 4] > > > x = x * 2 > > > x [1, 2, 3, 4, 1, 2, 3, 4]
成员测试运算符in可用于测试列表中是否包含某个元素。
> > > 3 in [1, 2, 3] True > > > 3 in [1, 2, '3'] False
关系运算符可以用来比较两个列表的大小。
> > > [1, 2, 4] > [1, 2, 3, 5] #逐个比较对应位置的元素 #直到某个元素能够比较出大小为止 True > > > [1, 2, 4] == [1, 2, 3, 5] False
3.2.5 内置函数对列表的操作
除了列表对象自身方法之外,很多Python内置函数也可以对列表进行操作。
> > > x = list(range(11)) #生成列表 > > > import random > > > random.shuffle(x) #打乱列表中元素顺序 > > > x [0, 6, 10, 9, 8, 7, 4, 5, 2, 1, 3] > > > all(x) #测试是否所有元素都等价于True False > > > any(x) #测试是否存在等价于True的元素 True > > > max(x) #返回最大值 10 > > > max(x, key=str) #按指定规则返回最大值 9 > > > min(x) #最小值 0 > > > sum(x) #所有元素之和 55 > > > len(x) #列表元素个数 11 > > > list(zip(x, [1]*11)) #多个列表元素重新组合 [(0, 1), (6, 1), (10, 1), (9, 1), (8, 1), (7, 1), (4, 1), (5, 1), (2, 1),(1, 1), (3, 1)] > > > list(zip(range(1, 4))) #zip()函数也可以用于一个序列或迭代对象 [(1, ), (2, ), (3, )] > > > list(zip(['a', 'b', 'c'], [1, 2])) #如果两个列表不等长,以短的为准 [('a', 1), ('b', 2)] > > > list(enumerate(x)) #把enumerate对象转换为列表 #也可以转换成元组、集合等 [(0, 0), (1, 6), (2, 10), (3, 9), (4, 8), (5, 7), (6, 4), (7, 5), (8, 2),(9, 1), (10, 3)]
3.2.6 列表推导式
列表推导式可以使用非常简洁的方式对列表或其他可迭代对象的元素进行遍历、过滤或再次计算,快速生成满足特定需求的新列表。列表推导式的语法形式为:
[expression for expr1 in sequence1 if condition1 for expr2 in sequence2 if condition2 for expr3 in sequence3 if condition3 ... for exprN in sequenceN if conditionN]
列表推导式在逻辑上等价于一个循环语句,只是形式上更加简洁。例如:
> > > aList = [x+x for x in range(30)]
相当于:
> > > aList = [] > > > for x in range(30): aList.append(x+x)
再例如,
> > > freshFruit = [' banana', ' loganberry ', 'passion fruit '] > > > aList = [w.strip() for w in freshFruit] > > > aList ['banana', 'loganberry', 'passion fruit']
等价于下面的代码:
> > > aList = [] > > > for item in freshFruit: aList.append(item.strip()) #字符串方法strip()用来删除两侧的空格
例3-1 使用列表推导式实现嵌套列表的平铺。
基本思路:先遍历列表中嵌套的子列表,然后再遍历子列表中的元素并提取出来作为最终列表中的元素。
> > > vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] > > > [num for elem in vec for num in elem] [1, 2, 3, 4, 5, 6, 7, 8, 9]
在这个列表推导式中有两个循环,其中第一个循环可以看作是外循环,执行得慢;而第二个循环可以看作是内循环,执行得快。上面代码的执行过程等价于下面的写法:
> > > vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] > > > result = [] > > > for elem in vec: for num in elem: result.append(num)
> > > result [1, 2, 3, 4, 5, 6, 7, 8, 9]
例3-2 在列表推导式中使用if过滤不符合条件的元素。
基本思路:在列表推导式中可以使用if子句对列表中的元素进行筛选,只在结果列表中保留符合条件的元素。
1)下面的代码可以列出当前文件夹下所有Python源文件,其中os.listdir()用来列出指定文件夹中所有文件和子文件夹清单,字符串方法endswith()用来测试字符串是否以指定的字符串结束。
> > > import os > > > [filename for filename in os.listdir('.') #'.’表示当前文件夹 if filename.endswith(('.py', '.pyw'))]
2)下面的代码用于从列表中选择符合条件的元素组成新的列表。
> > > aList = [-1, -4, 6, 7.5, -2.3, 9, -11] > > > [i for i in aList if i >0] #保留所有大于0的数字 [6, 7.5, 9]
3)下面的代码使用列表推导式查找列表中最大元素的所有位置。
> > > from random import randint > > > x = [randint(1, 10) for i in range(20)] #20个介于[1, 10]的整数 > > > x [10, 2, 3, 4, 5, 10, 10, 9, 2, 4, 10, 8, 2, 2, 9, 7, 6, 2, 5, 6] > > > m = max(x) > > > [index for index, value in enumerate(x) if value == m] #最大整数的所有出现位置 [0, 5, 6, 10]
例3-3 在列表推导式中同时遍历多个列表或可迭代对象。
列表推导式1:
> > > [(x, y) for x in [1, 2, 3] for y in [3, 1, 4] if x ! = y] [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
列表推导式2:
> > > [(x, y) for x in [1, 2, 3] if x==1 for y in [3, 1, 4] if y! =x] [(1, 3), (1, 4)]
对于包含多个循环的列表推导式,一定要清楚多个循环的执行顺序或“嵌套关系”。例如,列表推导式2等价于:
> > > result = [] > > > for x in [1, 2, 3]: if x==1: for y in [3, 1, 4]: if y! =x: result.append((x, y)) > > > result [(1, 3), (1, 4)]
3.2.7 切片
除了适用于列表之外,切片还适用于元组、字符串、range对象,但列表的切片操作具有最强大的功能。不仅可以使用切片来截取列表中的任何部分并返回得到一个新列表,也可以通过切片来修改和删除列表中的部分元素,甚至可以通过切片操作为列表对象增加元素。
在形式上,切片使用两个冒号分隔的3个数字来完成。
[start:end:step]
其中第一个数字start表示切片开始位置,默认为0;第二个数字end表示切片截止(但不包含)位置(默认为列表长度);第三个数字step表示切片的步长(默认为1)。当start为0时可以省略,当end为列表长度时可以省略,当step为1时可以省略,省略步长时还可以同时省略最后一个冒号。另外,当step为负整数时,表示反向切片,这时start应该在end的右侧才行。
(1)使用切片获取列表部分元素
使用切片可以返回列表中部分元素组成的新列表。当切片范围超出列表边界时,不会因为下标越界而抛出异常,而是简单地在列表尾部截断或者返回一个空列表,代码具有更强的健壮性。
> > > aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] > > > aList[::] #返回包含原列表中所有元素的新列表 [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] > > > aList[::-1] #返回包含原列表中所有元素的逆序列表 [17, 15, 13, 11, 9, 7, 6, 5, 4, 3] > > > aList[::2] #从下标0开始,隔一个取一个 [3, 5, 7, 11, 15] > > > aList[3:6] #指定切片的开始和结束位置 [6, 7, 9] > > > aList[0:100] #切片结束位置大于列表长度时,从列表尾部截断 [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
(2)使用切片为列表增加元素
可以使用切片操作在列表任意位置插入新元素,不影响列表对象的内存地址,属于原地操作。
> > > aList = [3, 5, 7] > > > aList[len(aList):] [] > > > aList[len(aList):] = [9] #在列表尾部增加元素 > > > aList[:0] = [1, 2] #在列表头部插入多个元素 > > > aList[3:3] = [4] #在列表中间位置插入元素 > > > aList [1, 2, 3, 4, 5, 7, 9]
(3)使用切片替换和修改列表中的元素
> > > aList = [3, 5, 7, 9] > > > aList[:3] = [1, 2, 3] #替换列表元素,等号两边的列表长度相等 > > > aList [1, 2, 3, 9] > > > aList[3:] = [4, 5, 6] #切片连续,等号两边的列表长度可以不相等 > > > aList [1, 2, 3, 4, 5, 6] > > > aList[::2] = ['a', 'b', 'c'] #隔一个修改一个 > > > aList ['a', 2, 'b', 4, 'c', 6] > > > aList[::2] = [1] #切片不连续时等号两边列表长度必须相等 ValueError: attempt to assign sequence of size 1 to extended slice of size 3
(4)使用切片删除列表中的元素
> > > aList = [3, 5, 7, 9] > > > aList[:3] = [] #删除列表中前3个元素 > > > aList [9]
另外,也可以结合使用del命令与切片结合来删除列表中的部分元素,并且切片元素可以不连续。
> > > aList = [3, 5, 7, 9, 11] > > > del aList[:3] #切片元素连续 > > > aList [9, 11] > > > aList = [3, 5, 7, 9, 11] > > > del aList[::2] #切片元素不连续,隔一个删一个 > > > aList [5, 9]