上个月11月21日完成了字符串章节的阅读,心中非常欢喜,连滚带爬总算翻过了一道坎,想必接下来的内容能顺利些,哪成想要读完容器这章,也势必要再费一番周折。消极的话到此为止,还是继续正文摘要吧。
本章主讲内容是数据容器(Container),包括字符串、由 range() 函数生成的等差数列、列表(List)、元组(Tuple)、集合(Set)、字典(Dictionary)。根据不同的分类方法,这些容器又分为可变容器(Mutable)和不可变容器(Immutable)。可变的有列表、集合、字典;不可变的有字符串、range() 生成的等差数列、元组。集合,又分为 Set 和 Frozen Set;其中,Set 是可变的,Frozen Set 是不可变的。字符串、由 range() 函数生成的等差数列、列表、元组是有序类型(Sequence Type),而集合与字典是无序的。另外,集合没有重合元素。分类汇总图表如下:
迭代
数据容器里的元素是可以被迭代的(Iterable),为便于处理,它们其中包含的元素,可以被逐个访问。由于数据容器的可迭代性,加上操作符 in,在 Python 语言里写循环格外容易且方便(以字符串这个字符的容器作为例子):
for c in 'Python':
print(c)
P
y
t
h
o
n
在 Python 中,简单的 for 循环,只需要指定一个次数就可以了,因为有 range() 这个函数:
for i in range(10):
print(i)
0
1
2
3
4
5
6
7
8
9
Python 也不只有 for 循环,还有 while 循环,在必要的时候可以写复杂的计数器。
列表(List)
列表和字符串一样,是个有序类型(Sequence Type)的容器,其中包含着有索引编号的元素。列表中的元素可以是不同类型。
列表的生成
生成一个列表,有以下四种方式:
a_list = []
b_list = [1, 2, 3]
list(), or list(iterable) # 这是 Type Casting
[(expression with x) for x in iterable]
列表的操作符
列表的操作符和字符串一样,因为它们都是有序容器。列表的操作符有:
拼接:+(与字符串不一样的地方是,不能用空格 ' ' 了)
复制:*
逻辑运算:in 和 not in,<、<=、>、>=、!=、==
两个列表可以被比较,即,可以进行逻辑运算;比较方式也跟字符串一样,从两个列表各自的第一个元素开始逐个比较,“一旦决出胜负马上停止”
根据索引提取列表元素
列表当然也可以根据索引操作,但由于列表是可变序列,所以,不仅可以提取,还可以删除,甚至替换。
运行下列语句:
import random
n = 3
a_list = [random.randrange(65, 91) for i in range(n)]
b_list = [chr(random.randrange(65, 91)) for i in range(n)]
print(a_list)
c_list = a_list + b_list + a_list * 2
print(c_list)
print()
# 根据索引提取(Slicing)
print(c_list[3]) # 返回索引值为 3 的元素值
print(c_list[:]) # 相当于 c_list,返回整个列表
print(c_list[5:]) # 从索引为 5 的值开始直到末尾
print(c_list[:3]) # 从索引 0 开始,直到索引 3 之前(不包括 3)
print(c_list[2:6]) # 从索引 2 开始,直到索引 6 之前(不包括 6)
print()
# 根据索引删除
del c_list[3]
print(c_list) # del 是个命令,del c_list[3] 是一个语句;不能这么写:print(del c_list[3])
del c_list[5:8]
print(c_list)
print()
# 根据索引替换
c_list[1:5:2] = ['a', 2] # s[start:stop:step] = t,跟 range 的三个参数类似;
# len(t) = len([start:stop:step]) 必须为真
输出结果如下:
[77, 66, 79]
[77, 66, 79, 'L', 'Z', 'R', 77, 66, 79, 77, 66, 79]
L
[77, 66, 79, 'L', 'Z', 'R', 77, 66, 79, 77, 66, 79]
['R', 77, 66, 79, 77, 66, 79]
[77, 66, 79]
[79, 'L', 'Z', 'R']
[77, 66, 79, 'Z', 'R', 77, 66, 79, 77, 66, 79]
[77, 66, 79, 'Z', 'R', 77, 66, 79]
[77, 'a', 79, 2, 'R', 77, 66, 79]
需要注意,列表(List)是可变序列,而字符串(str)是不可变序列,对字符串来说,虽然也可以根据索引提取,但没办法根据索引删除或者替换。
字符串常量(String Literal)是不可变有序容器,虽然字符串也有一些 Methods 可用,但那些 Methods 都不改变它们自身,而是在操作后返回一个值给另外一个变量。而对于列表这种可变容器,我们可以对它进行操作,结果是它本身被改变了。见如下示例:
s = 'Python'
L = list(s)
print(s)
print(L)
del L[2]
print(L) # 用 del 对 L 操作之后,L 本身少了 1 个元素
返回结果
Python
['P', 'y', 't', 'h', 'o', 'n']
['P', 'y', 'h', 'o', 'n']
列表可用的内建函数
列表和字符串都是容器,它们可使用的内建函数也其实都是一样的:
len()
max()
min()
示例:
import random
n = 3
# 生成 3 个随机数,构成一个列表
a_list = [random.randrange(65, 91) for i in range(n)]
b_list = [chr(random.randrange(65, 91)) for i in range(n)]
print(a_list)
print(b_list)
# 列表可以使用操作符 + 和*
c_list = a_list + b_list + a_list * 2
print(c_list)
a_list *= 3
print(a_list)
# 内建函数操作 len()、max()、min()
print(len(c_list))
print(max(b_list)) # 内建函数内部做了异常处理,可以比较字符和数字 —— 初学者最讨厌这种事情了……
print(min(b_list)) # 注意,max() 和 min() 应用的是 b_list, len() 应用的是 c_list —— 请自行找到对应的 list 进行分析。
print('X' not in b_list)
返回结果
[89, 84, 85]
['X', 'B', 'X']
[89, 84, 85, 'X', 'B', 'X', 89, 84, 85, 89, 84, 85]
[89, 84, 85, 89, 84, 85, 89, 84, 85]
12
X
B
False
Methods
字符串常量和 range() 都是不可变的(Immutable);而列表则是可变类型(Mutable type),所以,它最起码可以被排序 —— 使用 sort() method ,如果列表中的元素全都是由字符串构成的,当然也可以排序.
注意:被比较的元素应该是同一类型 ,不是由同一种数据类型元素构成的列表,不能使用 sort() Method。
可变序列还有一系列可用的 Methods:a.append(),a.clear(),a.copy(),a.extend(t),a.insert(i,x),a.pop([i]),a.remove(x),a.reverse()
import random
示例:
n = 3
a_list = [random.randrange(65, 91) for i in range(n)]
b_list = [chr(random.randrange(65, 91)) for i in range(n)]
print(a_list)
c_list = a_list + b_list + a_list * 2
print(c_list)
# 在末尾追加一个元素
c_list.append('100')
print(c_list)
# 清空序列
print()
print(a_list)
a_list.clear()
print(a_list)
print()
# 拷贝一个列表
d_list = c_list.copy()
print(d_list)
del d_list[6:8]
print(d_list)
print(c_list) # 对一个拷贝操作,不会更改 “原件”
print()
# 演示拷贝 .copy() 与赋值 = 的不同
e_list = d_list
del e_list[6:8]
print(e_list)
print(d_list) # 对 e_list 操作,相当于对 d_list 操作
# 在末尾追加一个列表
print()
print(a_list)
a_list.extend(c_list) # 相当于 a_list += c_list
print(a_list)
# 在某索引位置插入一个元素
print()
print(a_list)
a_list.insert(1, 'example') # 在索引 1 的位置插入 'example'
a_list.insert(3, 'example') # 在索引 3 的位置插入 'example';
print(a_list)
# 排序
# a_list.sort() 这一句会出错,因为当前列表中的元素,是 int 和 str 混合的。
print()
print(a_list)
a_list.reverse()
print(a_list)
x = a_list.reverse() # reverse() 只对当前序列操作,并不返回一个逆序列表;返回值是 None
print(x)
返回结果
[90, 88, 73]
[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73]
[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']
[90, 88, 73]
[]
[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']
[90, 88, 73, 'T', 'N', 'Y', 73, 90, 88, 73, '100']
[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']
[90, 88, 73, 'T', 'N', 'Y', 88, 73, '100']
[90, 88, 73, 'T', 'N', 'Y', 88, 73, '100']
[]
[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']
[90, 88, 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']
[90, 'example', 88, 'example', 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']
[90, 'example', 88, 'example', 73, 'T', 'N', 'Y', 90, 88, 73, 90, 88, 73, '100']
['100', 73, 88, 90, 73, 88, 90, 'Y', 'N', 'T', 73, 'example', 88, 'example', 90]
None
命令del,Method a.pop([i])和a.remove(x)都与删除单个元素相关,请注意它们之间的区别。
import random
n = 3
a_list = [random.randrange(65, 91) for i in range(n)]
print(a_list)
# 插入
print()
a_list.insert(1, 'example') # 在索引 1 的位置插入 'example'
# 删除
print()
print(a_list)
a_list.remove('example') # 去除 'example' 这个元素,如果有多个 'example',只删除第一个
print(a_list)
# pop() 删除并返回被删除的值
print()
print(a_list)
p = a_list.pop(2) # 去除索引为 2 的元素,且返回元素的值,赋值给 p
print(a_list)
print(p)
# pop() 与 del,或者 remove() 的区别
print()
a_list.insert(2, 'example')
a_list.insert(2, 'example')
print(a_list)
del a_list[2]
print(a_list)
print()
print(a_list.remove('example')) # a_list.remove() 这个 Method 的返回值是 None
print(a_list)
返回结果:
[88, 84, 69]
[88, 'example', 84, 69]
[88, 84, 69]
[88, 84, 69]
[88, 84]
69
[88, 84, 'example', 'example']
[88, 84, 'example']
None
[88, 84]
容器特性总结
路漫漫其修远兮,唯有耐心随其愿。加油!