列表

序列是 Python 中最基本的数据结构,序列中每一个元素都会被分配一个用于表示其位置的数字,即索引。序列的索引从 0 开始。Python 中有 6 个序列类型,常用的是列表和元组。所有序列类型都可以进行索引、切片、加、乘和成员检查的操作。

列表是最常用的数据类型,通常使用在方括号中使用逗号分割值的形式定义,列表的数据项不需要有相同的数据类型。空列表可以使用[],或者list()函数来创建。

list1 = ['a', 'b', 'c']
list2 = [1, 2, 3, 4, 5]
list3 = ['a', 'b', 'c', 1, 2, 3]
list4 = list()
list5 = []

列表使用索引来访问相应的元素,并且可以对元素进行修改。要删除列表元素需要使用del语句。例如:

list1 = [1, 2, 3, 4, 5]
print(list1[2]) # 输出 3
list1[2] = 9
print(list1) # 输出[1, 2, 9, 4, 5]
del list1[3]
print(list1) # 输出[1, 2, 9, 5]

在使用索引访问列表元素时需注意超范围访问,即下标越界,此时 Python 会抛出IndexError。列表可以嵌套来定义多维列表,例如:[[1, 2, 3], ['a', 'b', 'c']]

列表操作

使用+可以拼接组合两个列表,*可以将列表的内容重复指定次数,in可以用来判断元素是否存在于列表中,搭配for可以用来进行列表内容的迭代。

使用for来进行列表迭代的时候,默认是直接迭代列表中的值,如果需要获取元素的索引,则需要使用enumerate()函数来辅助,该函数是一个生成器,每次会返回一个元组,其中第一个元素为索引值,第二个元素为索引对应的值,使用格式一般为for index, element in enumerate(list)

对于列表,Python 提供了以下函数和方法。

  • len(list),返回列表元素个数。
  • max(list),返回列表中元素的最大值。
  • min(list),返回列表中元素最小值。
  • list(tuple),将元组转换为列表。
  • list.append(obj),将obj添加到列表末尾。
  • list.count(obj),统计列表中obj出现的次数。
  • list.extend(seq),在列表末尾追加另一个列表中的元素。
  • list.index(obj),返回obj在列表中的索引。
  • list.insert(index, obj),将obj插入到列表的指定索引位置。
  • list.pop(index=-1),移除列表中指定索引的元素,默认最后一个元素,并返回其值。
  • list.remove(obj),移除列表中obj的第一个匹配项。
  • list.reverse(),返回反转后的列表。
  • list.sort(cmp=None, key=None, reverse=False),对原列表进行排序。
  • list.clear(),清空列表。
  • list.copy(),复制列表。

切片

切片是 Python 中强大功能之一,常用于操作序列和字符串。切片与序列和字符串的索引访问类似,都是使用方括号,但格式不同。切片可以包含三个数字,完整格式为:[起始索引:结束索引:步进]。这三个数字可正可负,分别表示不同的含义。切片中的这三个数字有些时候可以省略,但起始索引和结束索引之间的冒号不可以省略。

列表与字符串中每个元素都对应一正一负两个索引值,其中正值索引是从左向右的索引,负值索引是从右向左的索引。所以切片中步进值为正表示从左向右截取,负值则表示从右向左截取。

切片应用于列表,其返回结果也还是列表;应用于字符串,其返回结果依旧是字符串。

以下通过一些示例,来说明不同切片值的含义。假设有一个列表a = [1, 2, 3, 4, 5, 6, 7],就可以有以下操作。

  • a[:],返回完整列表内容。
  • a[4:],返回索引从 4 开始以后的全部数据,即[5, 6, 7]
  • a[:3],返回索引$[0,3)$的元素组成的集合,即[1, 2, 3]
  • a[:-5],返回 0 到从右数第五个元素,即[1, 2]
  • a[:5:2],以步进 2 返回 0 到 5 的元素,即返回索引 0、2、4,即[1, 3, 5]
  • a[:4:-1],步进为负值,从右侧开始取到索引 4 的前一个元素位置,即[7, 6]
  • a[:10],返回索引$[0, 10)$的的全部数据,超过实际索引后也不会报错。
  • a[::-1],返回逆序列表,即[7, 6, 5, 4, 3, 2, 1]

任何情况下都需要记住,结束索引始终不会包含在切片中。对切片进行操作,会改变列表的结构和值。下面通过几个示例来演示对切片赋值的操作结果。

list = ['a', 'b', 'c', 'd', 'e', 'f']
list[len(list):] = ['g', 'h'] # 将指定列表添加到原列表的末尾,操作完后的 list 是['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list[1:4] = [1, 2] # 用指定列表替换原列表中切片部分,操作完成后的 list 是['a', 1, 2, 'e', 'f', 'g', 'h']
list[:] = '' # 替换掉原列表的内容,而不是创建一个新的对象

列表推导式

列表推导式提供了创建列表的简单途径,通常用于将一些操作应用于序列的每个元素,用其获得的结果作为生成新列表的元素,或者根据确定的条件创建子序列。用简单的话说,列表推导式相当于 Java 语言中 stream 操作的mapfilter的组合。列表推导式都在for关键字后跟一个表达式,之后还会跟零到多个for或者if子句;返回结果是一个根据表达式从其后的 forif 上下文环境中生成出来的列表。如果希望返回元组,则需要用括号包裹推导式。

Info

列表推导式是一项十分经典的 Pythonic 操作。

下面看一些示例。

vec = [2, 4, 6]
list = [3*x for x in vec]
print(list) # 会输出[6, 12, 18]
list = [[x, x**2] for x in vec]
print(list) # 会输出[[2, 4], [4, 16], [6, 36]]
list = [3*x for x in vec if x > 3]
print(list) # 会输出[12, 18]

列表推导式中可以使用复杂表达式和嵌套函数。下面给出一些技巧性的演示,读者可以自行在解析器中尝试运行。

vec1 = [2, 4, 6]
vec2 = [4, 3, -9]
print([x*y for x in vec1 for y in vec2])
print([vec1[i]\*vec2[i] for i in range(len(vec1))])
print([str(round(355/113, i) for i in range(1, 6))])

例如有以下矩阵:

matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
]

可以使用以下推导式完成矩阵的旋转:

new_matrix = [[row[i] for row in matrix] for i in range(4)]