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 文档(选读)
Python 文档中并没有这样介绍元组。Python 文档中,对于有二个及以上元素的元组而言,没有括号的元组输入才是常态,而括号只是元组防止歧义的工具。只有对于空元组和一个元素的元组,才必须要加括号。


理解 Python 文档的一个简单方法是:把逗号当成优先级比“赋值”高的运算符。比如:1, 2, 3运算的结果是返回一个元素为123的元组。

然后,括号可以截断这样的运算符。

为了和一般的表达式区分开,创建只有一个元素的元组时,需要在元素后加一个括号,比如:

d = (0,)

为了方便起见,有2个或多个元素的元组,在不引发歧义的情况下,可以不加括号。

e = 0, 1
f = (0, 1), 2  #此时,作为 f 第一个元素的元组,必须要加括号。

前面赋值方法章节提到的序列解包,就是对元组进行的操作。

list函数 / tuple函数[编辑 | 编辑源代码]

类似 intfloat 等函数的行为,list 函数 / tuple 函数可以用于创建列表/元组。

创建空列表/空元组[编辑 | 编辑源代码]

a = list()
b = tuple()

不传入任何参数的情况下,生成的是空列表/空元组。

将可迭代对象转换为列表/元组[编辑 | 编辑源代码]

可迭代对象
在 Python 中,有一些对象被称作是是“可迭代对象”。这类对象一般包含多个元素,而且有一个明显的迭代顺序。列表、元组和字符串就是可迭代对象。
可以简单认为,既有多个元素,又有顺序的对象都是可迭代对象。
a = list((1, 2, 3))
b = tuple([1, 2, 3])

任何可迭代对象都可以作为 list 函数 / tuple 函数的参数。

通用序列操作[编辑 | 编辑源代码]

大多数序列类型,包括可变序列和不可变序列都支持下面的操作。列表和元组就支持下面的操作。

加法与整数乘法[编辑 | 编辑源代码]

提示
拼接不可变序列时,每做一次加法都会生成一个新对象。比如:
(1,)+(2,)+(3,)

Python 会先计算(1,)+(2,),在内存中存入(1, 2);然后再计算(1, 2)+(3,),在内存中存入(1, 2, 3)。在计算时存储的总内容为: (1,)(2,)(3,)(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 作索引时,总会返回一个子列表或子元组。

in / not in[编辑 | 编辑源代码]

print(1 in a, 1 not in b) #可以用比较表达式 in / not in 判断元素是否在列表/元组中。

和字符串不同,innot 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) 和字符串不同,不支持 startstop 两个参数。它会输出整个列表中,该元素的个数。

用推导式创建列表/元组[编辑 | 编辑源代码]

除上面的操作之外,一个常用的操作是用推导式创建列表或元组。以下是一个简单示例:

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 = [0, 1]
s[1] = 2

print(s)
输出
[0, 2]
s[i:j] = t 按给出的子序列替换可变序列的元素。
s = [0, 1, 2]
s[1:] = [2, 3, 4]

print(s)
输出
[0, 2, 3, 4]
del s[i:j] 删除序列中,在该子序列中的元素。
等同于s[i: j] = []
s = [0, 1, 2]
del s[1:]

print(s)
输出
[0]
s[i:j:k] = t 替换可变序列的子序列。
此时t的长度必须与子序列长度相匹配。
如果不匹配,会报错 ValueError
s = [0, 1, 2]
s[::2] = [3, 4]

print(s)
输出
[3, 1, 2]
del s[i:j:k] 删除序列中,在该子序列中的元素。
s = [0, 1, 2]
del s[::2]

print(s)
输出
[1]
s.append(x) 将元素 x 添加到序列的末尾。
等同于 s[len(s):len(s)] = [x]
s = [0, 1, 2]
s.append(3)

print(s)
输出
[0, 1, 2, 3]
s.clear() s 中移除所有项
等同于 del s[:]
s = [0, 1]
s.clear()

print(s)
输出
[]
s.copy() 创建并返回 s 的副本。
等同于 s[:]
该方法的作用将在之后讲解。
s = [0, 1]
s1 = s.copy()

print(s1)
输出
[0, 1]
s.extend(t) 用可迭代对象 t 中的元素扩展 s
s = [0]
s.extend((1,2))

print(s)
输出
[0, 1, 2]
s += t s.extend(t)
这里不能简单理解为 s = s + t
s = [0]
s +=
"Py"
print(s)
输出
[0, 'P', 'y']
s *= n 结果等同于 s = s * n
s = [0]
s *= 3

print(s)
输出
[0, 0, 0]
s.insert(i, x) 在索引i处插入元素。使 s[i] == x
若索引 i < -len(s),则会插入在序列
开头。若索引 i >= len(s),则会插入
在序列结尾。
s = [0, 1, 2]
s.insert(1, "1")

print(s)
输出
[0, '1', 1, 2]
s.pop(i) 移除索引为 i 的元素,并返回该元素的
值。如果索引为 i 的元素不存在,则会
报错:IndexError: pop index out of
range
s = [0, True, 'b']
a = s.pop(1)

print(a)
print(s)
输出
True
[0, 'b']
s.pop() i 未给出,pop方法默认移除最后一
个元素。若序列为空序列,则会报错:
IndexError: pop from empty list
s = [0, True, 'b']
a = s.pop()

print(a)
print(s)
输出
'b'
[0, True]
s.remove(x) 删除 s 中第一个等于 x 的元素。若没
有符合标准的元素,则会报错:
ValueError: list.remove(x): x not
in list
s = [0, True, 'b', True]
s.remove(True)

print(s)
输出
[0, 'b', True]
s.reverse() 反转列表。
s = [0, 1, 2]
s.reverse()

print(s)
输出
[2, 1, 0]

列表:可变序列[编辑 | 编辑源代码]

列表是可变序列,元组是不可变序列。可变序列均支持此表格中的操作。

列表的排序[编辑 | 编辑源代码]

list.sort(*, key=None, reverse=False)[编辑 | 编辑源代码]

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 语言中关于指针的内容,可能很容易就能猜到原因。这里不多讲解原因,我们讲讲如何规避这样的问题。

表格中列举的可变序列操作中,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]]

此时,可以使用推导式:

a = [[0,1], [2,3]]
b = [x.copy() for x in a]
b[1][0] = 0
print(a)  #输出:[[0, 1], [2, 3]]

就解决了问题。解决此类问题的方法,基本上就是用 copy 方法和推导式的嵌套。