User:Xyy23330121/Python/列表和元组
Python 中还提供了两种好用的序列:列表和元组。其中列表是可变序列,而元组和字符串一样是不可变序列。
用方括号括起来,不被解释为索引的的内容会被视为列表,列表是由一系列变量组成的,每个变量之间由逗号,
分隔。
a = [0, 1, 'string'] #创建一个列表,列表中可以包含任意的不同变量。
print(a) #输出:[0, 1, 'string']
b = [] #可以创建空列表。
需要特别强调,列表中的元素也可以是一个列表。
c = [[0, 1], 2]
用括号括起来,由多个用逗号隔开的值组成的是元组。
a = (0, 1, 'string') #创建一个元组,元组中可以包含任意的不同变量。
print(a) #输出:(0, 1, 'string')
b = () #可以创建空元组
需要特别强调,元组中的元素也可以是一个元组或列表。
c = ((0, 1), 2)
理解 Python 文档的一个简单方法是:把逗号当成优先级比“赋值”高的运算符。比如:1, 2, 3
运算的结果是返回一个元素为1
、2
和3
的元组。
为了和一般的表达式区分开,创建只有一个元素的元组时,需要在元素后加一个括号,比如:
d = (0,)
为了方便起见,有2个或多个元素的元组,在不引发歧义的情况下,可以不加括号。
e = 0, 1
f = (0, 1), 2 #此时,作为 f 第一个元素的元组,必须要加括号。
前面赋值方法章节提到的序列解包,就是对元组进行的操作。
类似 int
、float
等函数的行为,list
函数 / tuple
函数可以用于创建列表/元组。
a = list()
b = tuple()
不传入任何参数的情况下,生成的是空列表/空元组。
可以简单认为,既有多个元素,又有顺序的对象都是可迭代对象。
a = list((1, 2, 3))
b = tuple([1, 2, 3])
任何可迭代对象都可以作为 list
函数 / tuple
函数的参数。
大多数序列类型,包括可变序列和不可变序列都支持下面的操作。列表和元组就支持下面的操作。
a = (1,)
b = (2,)
c = (3,)
a+b+c
Python 会先计算a+b
,在内存中存入(1, 2)
;然后再计算(1, 2)+c
,在内存中存入(1, 2, 3)
。在计算时存储的总内容为:
a
b
c
(1, 2)
(1, 2, 3)
,即 8 个元组中元素的内存。通过模拟可以得出,运算时内存的占用,和元素所需总内存的平方是同阶的。
为此,如果要连续拼接此类对象,应当使用 Python 为这些对象提供的方法,或者转化为可变序列。比如:
- 拼接字符串时,应当先构建由字符串组成的列表,再用
str.join
方法。 - 拼接元组时,应当先将元组转化为列表,再用
+=
运算符扩展列表。 - 拼接bytes对象时,可以用
bytes.join
方法,也可以转化为bytearray再用+=
进行原地拼接。
+=
运算符扩展列表,参见下面的内容。关于 bytes 和 bytearray,参见“字符串和编码”章节。类似字符串,两个列表相加、或两个元组相加,返回的是拼接之后的结果;而列表或元组和整数n
作乘法,返回的是重复 n 次后的结果。
print([1,2] + [3]) #加法:返回拼接之后的结果。
print((1,2) + (3,))
print([1,2] * 3) #整数乘法:返回原列表/元组重复 n 次的结果。
print((1,2) * 3)
除此之外,还有许多操作和字符串的结果类似。
a,b = [1, 2, 3], (1, 2, 3)
print(len(a),len(b)) #可以通过 len() 得到列表/元组的长度。
print(a[2],b[2]) #列表/元组支持索引。索引的方式和字符串类似。
print(a[::2],b[::2])
但是,和字符串返回对应位置的单个字符组成的子字符串不同,在使用整数作索引时,列表或元组会返回对应位置的元素,而非返回子列表或子元组。只有使用 slice 作索引时,总会返回一个子列表或子元组。
print(1 in a, 1 not in b) #可以用比较表达式 in / not in 判断元素是否在列表/元组中。
和字符串不同,in
和 not in
只判断元素是否是列表/元组的元素,不会判断子列表/子元组是否在原列表/原元组内。比如:
print([0,1] in [0,1,2]) #输出:False
print((0,1) in (0,1,2)) #输出:False
print("01" in "012") #输出:True
a = (0, 1, 2)
b = [0, 1, 2]
print(max(a), min(b)) #输出:2 0
max
函数和 min
函数可以对序列中的元素进行比较。但这要求序列中所有元素之间两两可以比较。
a = (0, 1, 2, 2, 3, 2)
b = [0, 1, 2, 2, 3, 2]
print(a.index(2, 3, 5), b.count(2)) #输出:3 3
s.index(value[, start[, stop]])
的运行结果等同于 s[start:stop].index(value)
,会输出范围内第一个等于 value
的元素的索引。
s.count(value)
和字符串不同,不支持 start
和 stop
两个参数。它会输出整个列表中,该元素的个数。
除上面的操作之外,一个常用的操作是用推导式创建列表或元组。以下是一个简单示例:
iterable = "Python"
s = [x for x in iterable]
t = (x for x in iterable)
此时,print(s,t)
输出的结果是:
['P', 'y', 't', 'h', 'o', 'n'] ('P', 'y', 't', 'h', 'o', 'n')
对于 a for b in c
,Python 会把可迭代对象 c
中的第一个迭代元素赋值到 b
,然后把赋值后、表达式 a
的结果插入到列表或元组中。再把 c
中的第二个迭代元素赋值到 b
,然后把赋值后、表达式 a
的结果插入到列表或元组中。直到可迭代对象没有下一个迭代元素为止。
在 a for b in c
后面,还可以添加 if d
。此时,在给 b
赋值后,只有表达式 d
的结果为 True
,才会把表达式 a
的结果插入到列表或元组中。比如以下形式:
iterable = "Python"
s = [x for x in iterable if x in "Thoughts"]
print(s) #输出:['t', 'h', 'o']
运算 | 解释 | 示例 | |
---|---|---|---|
s[i] = x
|
给可变序列中,索引为 i 的元素赋值。如果索引为 i 的元素不存在,会报错:IndexError: list assignment index out of range
|
||
输出 | |||
s[i:j] = t
|
按给出的子序列替换可变序列的元素。 | ||
输出 | |||
del s[i:j]
|
删除序列中,在该子序列中的元素。 等同于 s[i: j] = [] 。
|
||
输出 | |||
s[i:j:k] = t
|
替换可变序列的子序列。 此时 t 的长度必须与子序列长度相匹配。如果不匹配,会报错 ValueError 。
|
||
输出 | |||
del s[i:j:k]
|
删除序列中,在该子序列中的元素。 | ||
输出 | |||
s.append(x)
|
将元素 x 添加到序列的末尾。等同于 s[len(s):len(s)] = [x] 。
|
||
输出 | |||
s.clear()
|
从 s 中移除所有项等同于 del s[:] 。
|
||
输出 | |||
s.copy()
|
创建并返回 s 的副本。等同于 s[:] 。该方法的作用将在之后讲解。 |
||
输出 | |||
s.extend(t)
|
用可迭代对象 t 中的元素扩展 s 。
|
||
输出 | |||
s += t
|
同s.extend(t) 。这里不能简单理解为 s = s + t 。
|
||
输出 | |||
s *= n
|
结果等同于 s = s * n 。
|
||
输出 | |||
s.insert(i, x)
|
在索引i 处插入元素。使 s[i] == x 。若索引 i < -len(s) ,则会插入在序列开头。若索引 i >= len(s) ,则会插入在序列结尾。 |
||
输出 | |||
s.pop(i)
|
移除索引为 i 的元素,并返回该元素的值。如果索引为 i 的元素不存在,则会报错: IndexError: pop index out of
|
||
输出 | |||
s.pop()
|
若 i 未给出,pop 方法默认移除最后一个元素。若序列为空序列,则会报错: IndexError: pop from empty list
|
||
输出 | |||
s.remove(x)
|
删除 s 中第一个等于 x 的元素。若没有符合标准的元素,则会报错: ValueError: list.remove(x): x not
|
||
输出 | |||
s.reverse()
|
反转列表。 | ||
输出 |
列表是可变序列,元组是不可变序列。可变序列均支持此表格中的操作。
list.sort()
方法会对列表中的元素进行排序。我们有以下示例:
s = [0, 3, 1, 4]
s.sort()
print(s) #输出:[0, 1, 3, 4]
s.sort(reverse = True)
print(s) #输出:[4, 3, 1, 0]
关于参数key
,我们将放在函数章节里学习。
在使用列表时,以下代码可能不会得到想象中的结果:
a = [0, 1]
b = a
b[1] = 0
print(a)
这段代码输出的结果是[0, 0]
!说明在更改 b
的时候,a
也被同步更改了!
以下代码的输出也有类似的问题:
a = [[]]*3
a[0].append(1)
print(a) #输出[[1],[1],[1]]
如果读者学习过 C 语言中关于指针的内容,可能很容易就能猜到原因。这里不多讲解原因,我们讲讲如何规避这样的问题。
在 #列表的一些方法 章节中,有一个 s.copy()
方法。它可以用于解决上述问题的。我们将代码改为:
a = [0, 1]
b = a.copy()
b[1] = 0
print(a) #输出:[0, 1]
就解决了问题。但这对于嵌套的列表而言不起作用,比如:
a = [[0,1], [2,3]]
b = a.copy()
b[1][0] = 0
print(a) #输出:[[0, 1], [0, 3]]
除 copy 方法外,我们还可以用推导式来复制列表。比如:
a = [0, 1]
b = [x for x in a]
b[1] = 0
print(a) #输出:[0, 1]
我们可以嵌套多个推导式,来复制嵌套的列表。比如:
a = [[0,1], [2,3]]
b = [[xx for xx in x] for x in a]
b[1][0] = 0
print(a) #输出:[[0, 1], [2, 3]]
就解决了问题。若已知列表是二维列表。也可以用:
a = [[0,1], [2,3]]
b = [x.copy() for x in a]
b[1][0] = 0
print(a) #输出:[[0, 1], [2, 3]]
也可以解决问题。
使用推导式方法加一点控制,我们事实上可以做任意维度嵌套列表的复制。以下内容在学习函数章节之前可能较难理解。
def copy(seq): return [(copy(obj) if isinstance(obj, list) else obj) for obj in seq]
a = [0,0,[1,1,[2,2,[3,3]]]]
b = copy(a)
b[2][2][2][0] = 4
print(a)
print(b)