Python中内置的三种常用数据结构:列表(list)、元组(tuple)和字典(dict)。这三种数据结构可用于保存多个数据项,可以保存大量数据。列表和元组是按顺序存放元素,每个元素有自己的索引,可通过索引访问元素,两者的区别在于,元组是不可修改的,列表可以修改。字典以 key-value(键值对)形式保存数据。这三种数据结构是编程中必不可少的内容。
一、序列介绍
序列是指一种包含多项数据的数据结构,序列包含的多个数据项(也叫成员)按顺序排列,可通过索引来访问成员。
常见的序列类型有:字符串、列表、元组。字符串和元组序列类型是不可变的,一旦创建就不能修改其所包含的成员。列表是可变序列,程序可以修改列表序列中的元素。在实际运用时,如果保存固定个数的数据项,而不需要修改时,就应该使用元组。
列表创建:使用方括号,元素之间用英文逗号分隔。也可将可迭代对象传递给内置函数list()来创建。
元组创建:使用圆括号,元素之间用英文逗号分隔。也可将可迭代对象传递给内置函数tuple()来创建。
二、 列表和元组的通用方法
只要不涉及到改变元素的操作,列表和元组的用法是通用的。
1、 通过索引使用元素
列表和元组的索引都是从0开始,第1个元素的索引为0,第2个元素的索引为1,......,以此类推;还可以使用负数索引,倒数第1个元素的索引为-1,倒数第2个元素的索引为-2,......,以此类推。
列表的元素相当于一个变量,程序可使用它的值,也可对元素赋值;元组的元素相当于一个常量,程序只能使用它的值,不能重新赋值。
示例如下:
name = ('python', 'c', 'java', 'go', 'linux') # 定义一个元组
print(name) # 输出整个元组
print(name[0]) # 访问元组第1个元素
print(name[1]) # 访问元组第2个元素
print(name[-1]) # 访问元组倒数第1个元素
print(name[-2]) # 访问元组倒数第2个元素
上面通过索引访问元组的方法同样适用于列表。
2、 子序列
字符串可通过切片获取一个子串,列表和元组同样也可使用索引获取中间一段,该方法中叫作slice(分片或切片),语法格式如下:
[start: end: step]
start和end两个索引可以使用正数或负数,负数表示从倒数开始。语法表示从start索引开始(包含),到end索引结束(不包含)。
step表示步长,也可使用负数。示例如下:
name = ['python', 'c', 'java', 'go', 'linux'] # 定义一个列表
print(name[2:4]) # 访问第3个到第5个(不包含)索引间的元素,输出:['java', 'go']
print(name[-3:-1]) # 访问倒数第3个到倒数第1个间的元素,输出:['java', 'go']
print(name[1:-2]) # 访问从第2个到倒数第2个间的元素,输出:['c', 'java']
print(name[1:4:2]) # 访问从第2个到第5个(不包含)、间隔为2的元素,输出:['c', 'go']
print(name[::-1]) # 反转列表
上面对列表进行切片的操作,同样适用于元组。
3、 列表和元组的加法运算
列表和列表相加,元组和元组相加,不能直接将列表和元组相加。
a_tuple = (1, 2)
b_tuple = (3, 4)
sum_tuple = a_tuple + b_tuple # 两个元组相加后,a_tuple和b_tuple并没有任何改变
print(sum_tuple) # 输出:(1, 2, 3, 4)
# a_tuple + [10, 20] # 元组和列表相加,报 TypeError 错误。
print([10, 20] + ['a', 'b']) # 计算两个列表相加,输出:[10, 20, 'a', 'b']
4、 列表和元组的乘法运算
列表和元组可以和整数N相乘,得到的结果是列表和元组的元素重复N次。示例如下:
print([1, 'a'] * 3) # 输出:[1, 'a', 1, 'a', 1, 'a']
print(('a', 'z') * 3) # 输出:('a', 'z', 'a', 'z', 'a', 'z')
列表和元组可以同时使用加法、乘法。一个简单示例如下:
# 创建一个表示一个月31天中的后缀字符元组,对于1、2、3是st、nd、rd,其他使用 th 代表。
order_endings = ('st', 'nd', 'rd') + \
('th',) * 17 + ('st', 'nd', 'rd') \
+ ('th',) * 7 + ('st',)
print(order_endings)
day = input("输入日期(1-31):")
day_int = int(day)
print(day + order_endings[day_int - 1])
在这个示例中 order_endings 得到一个合并后的元组,要注意 ('th',) 的写法,当元组只有一个元素时,要在这个唯一的元素后面加英文逗号,不加英文逗号就表示的是字符串,字符串和元组不能相加。运行示例如下:
输入日期(1-31):28
28th
5、in 运算符
in 运算符用于判断列表或元组是否包含某个元素,判断结果是 bool 型值。示例如下:
a_tuple = ('python', 'java', 'c++')
print('java' in a_tuple) # 输出:True
print('go' in a_tuple) # 输出:False
6、 长度、最大值和最小值
Python内置的len()、max()、min() 全局函数可用于获取列表或元组的长度、最大值和最小值。其中 max()、min() 要对元组、列表中的元素比较大小,因此传给 max()、min()函数的元组、列表的元素必须是相同类型并且可以比较大小。示例如下:
a_tuple = (100, 33, 88, 50, 106) # 数字类型元组,每个元素类型相同
print(len(a_tuple)) # 计算长度,输出:5
print(max(a_tuple)) # 计算最大值,输出:106
print(min(a_tuple)) # 计算最小值,输出:33
b_list = ['Python', 'Java', 'Linux', '世界'] # 字符串类型的列表,每个元素类型相同
print(len(b_list)) # 计算长度,输出:4
print(max(b_list)) # 计算最大值,输出:世界
print(min(b_list)) # 计算最小值,输出:Java
由上面示例可知,Python中字符串是可以比较大小的,按照字符串中每个字符对应的编码来比较字符串的大小。
7、 序列封包和序列解包
序列封包(Sequence Packing)和序列解包(Sequence Unpacking)功能,可支持下面两种赋值方式。
(1)、程序把多个值赋给一个变量时,Python会自动将多个值封装成元组。这种功能叫做序列封包。
(2)、程序允许将序列(字符串、元组、列表等)直接赋值给多个变量,此时序列的各元素会被依次赋值给每个变量(要求序列的元素个数和变量个数相等)。这种功能叫做序列解包。
示例如下:
res = 1, 2, 3 # 序列封包,封包成一个元组赋值给 res 变量
print(res) # 输出是序列:(1, 2, 3)
print(type(res)) # 查看类型是元组类型,输出:<class 'tuple'>
x, y, z = res # 序列解包,序列中的元素依次赋值给 x、y、z三个变量
在赋值时同时使用了序列封包和序列解包机制,可让赋值运算符同时将多个值赋给多个变量,例如:
x, y, z = 10, 20, 30
上面这行代码实际执行过程是:
(1)、先执行序列封包
xyz = 10, 20, 30
(2)、再执行序列解包
x, y, z = xyz
使用这种语法可实现交换变量的值,例如:
x, y, z = y, z, x
序列解包时,也可只解出部分,就是剩下的依然使用列表变量保存。使用这种方式解包时,可以在左边被赋值的变量之前添加“*”,这个变量就代表一个列表,可以保存多个集合元素。示例如下:
first, second, *rest = range(10)
print(first) # 输出:0
print(second) # 输出:1
print(rest) # 输出:[2, 3, 4, 5, 6, 7, 8, 9]
*_, last = range(10) # 解包时保存最后一个元素,其它元素丢弃
first, *middle, last = range(10) # 保存第一个和最后一个元素,中间元素统一保存到列表中
三、 使用列表
元组不可变,列表是可变的。元组支持的操作,列表基本上都支持;列表支持对元素的修改,而元组不支持。如果程序不修改元素,可使用元组替代列表会更安全。另外,同样元素的列表和元组,元组占用的内存空间要小一些。
1、 创建列表
创建列表可用方括号,也可用内置的 list() 函数来创建,list() 函数可用于将元组、区间(range)等对象转换为列表。示例如下:
a_tuple = ('python', 'java', 'go') # 创建元组
a_list = list(a_tuple) # 使用 list() 函数将元组转列表
print(a_list) # 输出是列表:['python', 'java', 'go']
print(range(5)) # 区间是一个可迭代对象,输出:range(0, 5)
print(list(range(5))) # 使用 list() 函数将区间转换为列表,输出:[0, 1, 2, 3, 4]
区间函数 range() 有3个参数,分别是 start, stop, step。省略 start 参数时,默认从0开始,stop 参数是区间的结尾,不能省略,在计算区间时不包含结尾。step 参数是步长,省略时默认为1,该参数可以是负数。在Python2中的 xrange() 函数与Python3版本中的 range() 相同,在Python2中的 range() 函数返回的是列表。
创建元组也有相应的内置函数 tuple(),该函数可用于将列表、区间(range)等对象转换为元组。示例如下:
a_list = ['python', 'java', 'go']
a_tuple = tuple(a_list) # 使用 tuple() 函数将列表转换为元组
b_tuple = tuple(range(10, 1, -2)) # 将区间对象转换为元组
2、 增加列表元素,append()、extend()、insert() 方法
列表的 append() 方法可把传入的参数追加到列表的最后面。append() 方法可接收单个值参数,也可接收元组、列表等参数,但是接收的元组、列表会当成单个元素,这样会形成在列表中嵌套列表、嵌套元组的情形。示例如下:
a_list = [1, '2', 3]
a_list.append('a') # 在列表中追加元素
print(a_list) # 输出是:[1, '2', 3, 'a']
a_list.append((4, '5')) # 追加一个元组,元组被当成一个元素
print(a_list) # 输出是:[1, '2', 3, 'a', (4, '5')]
a_list.append([6, '7']) # 追加列表,列表被当成一个元素
print(a_list) # 输出是:[1, '2', 3, 'a', (4, '5'), [6, '7']]
如果不希望列表、元组当成整体被追加到列表中,只是追加列表中的所有元素,则可使用 extend() 方法。示例如下:
b_list = [1, '2']
b_list.extend((3, '4')) # 参数是元组,但追加的仍是元组中的所有元素
print(b_list) # 输出是:[1, '2', 3, '4']
b_list.extend([5, '6']) # 追加列表中的所有元素
print(b_list) # 输出是:[1, '2', 3, '4', 5, '6']
b_list.extend(range(8, 10)) # 还可以追加区间中的所有元素
print(b_list) # 输出是:[1, '2', 3, '4', 5, '6', 8, 9]
在列表的指定位置插入元素,可使用 insert() 方法,示例如下:
c_list = [1, 2, 3, 4]
c_list.insert(2, 'p') # 在索引2处插入字符串
print(c_list) # 输出是:[1, 2, 'p', 3, 4]
c_list.insert(2, tuple('py')) # 在索引2处插入一个元组,元组被当成一个元素
print(c_list) # 输出是:[1, 2, ('p', 'y'), 'p', 3, 4]
3、 删除列表元素,del、remove()方法、clear()方法
del 语句是Python的一种语句,专用于删除操作,不仅可用于删除列表的元素,还可用于删除变量等。
del 语句可删除列表中的单个元素,也可直接删除列表的中间一段,示例如下:
a_list = [1, 'a', 2, 'b']
del a_list[2] # 删除列表第3个元素
print(a_list) # 输出是:[1, 'a', 'b']
del a_list[1:3] # 通过列表切片,可一次性删除多个元素,切片还可以设定步长
del 语句还可删除普通变量,示例如下:
name = 'michael'
del name # 删除name 变量
print(name) # 报错:NameError
remove() 方法删除列表元素时,不根据索引来删除元素,而是根据元素本身来删除。只删除在列表中第一个找到的元素。如果在列表中未找到要删除的元素,则报ValueError错误。示例如下:
c_list = [10, 'python', 20, 'java', 20, 'go', 'python']
c_list.remove(20) # 删除第一次找到的元素
print(c_list) # 输出:[10, 'python', 'java', 20, 'go', 'python']
clear() 方法表示清空列表的所有元素。示例如下:
c_list.clear()
print(c_list) # 输出空列表:[]
4、 修改列表元素
列表的元素可看做是变量,程序可对列表的元素赋值,这样就达到修改列表元素。通过索引对列表元素赋值,索引可以是正数索引,也可以是负数索引。还可以通过切片语法对列表中某一部分赋值,使用切片操作时,新赋值的元素个数可以不等于原来的元素个数。这相当于在给列表增加或删除元素。示例如下:
d_list = list(range(1,5))
d_list[2] = 30 # 修改列表单个元素的值,结果:[1, 2, 30, 4]
d_list[1] = [10, 20] # 修改列表,结果:[1, [10, 20], 30, 4]
d_list[-1] = 40 # 修改列表最后一个元素的值,结果:[1, [10, 20], 30, 40]
d_list[1:3] = ('a', 'b', 'c') # 通过切片索引修改,切片末尾不包含,结果:[1, 'a', 'b', 'c', 40]
列表的切片索引中的起始索引和结束索引相等时,切片的结果是空列表,利用这个特性,可为列表插入元素。
d_list[2:2] = ['x', 'y'] # 切片起始索引等于结束索引,结果是在列表中指定位置插入元素
print(d_list) # 输出是:[1, 'a', 'x', 'y', 'b', 'c', 40]
如果将列表中某一段赋值为空列表,就变成从列表中删除元素。
d_list[2:5] = [] # 结果是2、3、4的元素被删除:[1, 'a', 'c', 40]
当对切片赋值单个值时,如果这个值是字符串,Python会把这个字符串当成序列处理,字符串的每个字符都是一个元素。如下示例:
d_list[1:3] = 'python' # 对切片赋值字符串,结果:[1, 'p', 'y', 't', 'h', 'o', 'n', 40]
d_list[1:3] = 100 # 对切片赋值一个数字常量,会报错:TypeError
使用切片赋值时,还可指定 step 参数,如果指定 step 参数,则要求所赋值的列表元素个数与所替换的列表个数相等。如下所示:
d_list[1: 6: 2] = [10, 20, 30]
print(d_list) # 输出是:[1, 10, 'y', 20, 'h', 30, 'n', 40]
5、 列表的其他常用方法
列表的方法中还有很多以双下划线开头的方法,这些方法有特殊意义,不建议使用。除这些双下划线方法外,列表可用的方法可在命令行使用 dir(list) 查看。
dir(list) # 剔除双下划线方法后,输出如下:
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
这些方法中还有一些前面未说过的,同样是常用的方法:
(1)、count()方法:统计列表中某个元素出现的次数。
(2)、index()方法:判断某个元素在列表中出现的位置,如果该元素不在列表中,报 ValueError 错误。
(3)、pop()方法:将列表中最后一个元素取出,相当于出栈。默认取出列表最后一个元素。
(4)、reverse()方法:反转列表,将列表中的元素反向存放。
(5)、sort()方法:用于对列表元素排序。
(1)、 count()方法使用
a_list = [10, 20, [10, 20], 20, 30]
print(a_list.count(20)) # 统计20在列表中出现的次数,不统计在嵌套列表中出现的次数,输出:2
print(a_list.count([10, 20])) # 输出:1
(2)、index() 方法使用
index()方法查找某个元素在列表中的出现的位置,如果该元素没有出现,则报 ValueError错误。该方法还可以传入 start、end 参数,用于在列表指定的范围内搜索元素。示例如下:
print(a_list.index(20)) # 查找元素 20 第一次出现的位置,输出:1
print(a_list.index(20, 2)) # 从索引2开始,查找元素 20 第一次出现的位置。
print(a_list.index(10, 1, 4)) # 在索引1到索引4之间找元素 10第一次出现的位置,找不到,报:ValueError
(3)、 pop() 方法使用
列表的 pop() 方法可实现元素出栈功能。栈是一种特殊数据结构,可实现先进后出(FILO,First in Last out)和 先进先出(FIFO,First in First out)功能。在其他编程语言中,有一个入栈方法 push(),在Python中可使用 append 代替 push() 方法。
pop()方法从列表取出元素后,会返回取出的元素,并且列表中相应的元素被移除。示例如下:
stack = ['a', 'b']
stack.append('c') # 入栈一个元素
print(stack.pop()) # 出栈一个元素,默认取出列表最后一个元素,输出:c
print(stack) # 此时的列表已经没有 c 这个元素,输出:['a', 'b']
print(stack.pop(0)) # 传递索引给 pop() 方法,取出指定的元素,输出:a
(4)、 reverse()方法使用
reverse()方法反转列表中的所有元素顺序,该方法会对列表进行就地修改。示例如下:
a_list = list(range(5))
a_list.reverse() # 反转列表中的元素顺序
print(a_list) # 输出:[4, 3, 2, 1, 0]
a_list[::-1] # 使用切片索引也能反转列表,但不会就地修改原列表
(5)、 sort() 方法使用
列表的sort() 方法默认对列表的元素按大小排序,默认是从小到大(升序)排序。传递参数 reverse=True 时,表示从大到小排序。还可以传递 key 参数改变排序方式,key 参数需要的是一个函数名,排序的方式由这个函数的功能决定。示例如下:
num_list = [5, -2, 7, -9, 11]
num_list.sort() # 默认排序,从小到大
print(num_list) # 输出:[-9, -2, 5, 7, 11]
num_list.sort(reverse=True) # 传递参数 reverse=True 改变默认排序,从大到小排序
str_list = ['pyt', 'go', 'java', '走你,python']
str_list.sort() # 对字符串元素排序,默认按字符串包含的字符的编码来比较大小
print(str_list) # 输出:['go', 'java', 'pyt', '走你,python']
str_list.sort(key=len) # 给 key 参数传递函数名 len,表示元素长度排序
print(str_list) # 输出:['go', 'pyt', 'java', '走你,python']
str_list.sort(key=len, reverse=True) # 长度从大到小排序
print(str_list) # 输出:['走你,python', 'java', 'pyt', 'go']
在Python2中列表的 sort() 方法可传入一个比较大小的函数,该函数负责比较列表元素的大小。该函数有两个参数,该函数返回正整数时表示第一个参数大于第二个参数;返回负整数时,表示函数第一个参数小于第二个参数;返回0时,表示两个参数相等。示例如下:
# 在Python2 中执行
def len_cmp(x, y):
'''定义一个根据长度比较大小的函数'''
return 1 if len(x) > len(y) else (-1 if len(x) < len(y) else 0)
str_list.sort(len_cmp)
print(str_list) # 输出:['go', 'pyt', 'java', '走你,python']