终于进入到第五章数据结构部分了,python中常用的容器有列表、元组、集合和字典,今天主要了解的是最为常用的列表。
5. 数据结构
5.1. 列表的更多特性
对于列表的操作有很多,具体操作看官方文档就行,但是我们对列表有什么操作,是应该有个印象的,否则需要用到的时候不知道有这种操作,自然也谈不上查文档了。
- 通过
list()
或者中括号[]
来新建一个列表;通过clear()
删除列表中所有的元素 - 在列表的末尾添加一个元素x
append(x)
;在列表中间插入一个元素insert(i, x)
- 删除并返回列表中的最后一个元素
pop()
;移除列表中第一个值为 x 的元素remove(x)
- 返回列表中第一个值为 x 的元素的从零开始的索引,也就是查找x
index(x)
;若要对x计数,返回元素 x 在列表中出现的次数,则用count(x)
; - 对列表中的元素进行排序用
sort()
;反转列表中的元素则用reverse()
。
一些方法示例如下:
>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4) # 从位置4开始查找banana
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'
要注意到两点:
- 像
insert
,remove
或者sort
方法,只修改列表,没有打印出返回值——它们返回默认值None
,这是Python中所有可变数据结构的设计原则。 - 并非所有数据或可以排序或比较。 例如,
[None, 'hello', 10]
就不可排序,因为整数不能与字符串比较,而 None 不能与其他类型比较。 并且还存在一些没有定义顺序关系的类型。 例如,3+4j < 5+7j
就不是一个合法的比较。
5.1.1. 列表作为栈使用
堆栈即最后一个插入的元素,最先取出(“后进先出”)。添加一个元素到堆栈的顶端,使用 append()
;从堆栈顶部取出一个元素,使用 pop()
:
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]
5.1.2. 列表作为队列使用
列表也可以用作队列,其中先添加的元素被最先取出 (“先进先出”);然而列表用作这个目的相当低效。因为在列表的末尾添加和弹出元素非常快,但是在列表的开头插入或弹出元素却很慢 (因为所有的其他元素都必须移动一位)。
若要实现一个队列, collections.deque
被设计用于快速地从两端操作。例如:
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry") # Terry进队列
>>> queue.append("Graham") # Graham进队列
>>> queue.popleft() # 第一个元素Eric出列
'Eric'
>>> queue.popleft() # 第一个元素John出列
'John'
>>> queue # 现在的队列
deque(['Michael', 'Terry', 'Graham'])
5.1.3. 列表推导式
列表推导式是更简单的创建列表的方法。常见的用法是把某种操作应用于序列或可迭代对象的每个元素上,然后使用其结果来创建列表,或者通过满足某些特定条件元素来创建子序列。
例如,假设我们想创建一个平方列表,用for遍历的话,就像这样:
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
它就等价于下面这种列表推导式:
squares = [x**2 for x in range(10)]
列表推导式的结构是:由一对方括号[]
包含以下内容:一个表达式,后面跟一个 for
子句,然后是零个或多个 for
或 if
子句。
列表推导式的结果是:一个新列表,由对表达式依据后面的 for
和 if
子句的内容进行求值计算而得出。
举例来说,以下列表推导式会将两个列表中不相等的元素组合起来:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
它等价于:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
这里是更多的例子:
>>> vec = [-4, -2, 0, 2, 4]
>>> # 创建一个将原列表里面的值*2的新列表,
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # 去掉原列表中的负数
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # 对原列表中的所有元素应用一个函数(此处为绝对值函数)
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # 在每个元素上调用一个方法
>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # 创建一个二元的元组如(数字, 数字的平方)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # 元组必须带括号,否则会引发错误
>>> [x, x**2 for x in range(6)]File "<stdin>", line 1, in <module>[x, x**2 for x in range(6)]^
SyntaxError: invalid syntax
>>> # 用带有两个for的列表推导式,使一个二维列表变为一维列表
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
5.1.4. 嵌套的列表推导式
列表推导式中的初始表达式可以是任何表达式,包括另一个列表推导式。
考虑下面这个 3x4的矩阵,它由3个长度为4的列表组成:
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
下面的列表推导式将交换其行和列:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
注意遍历是先i后row。这里如果用zip()
可能会更简单点:
>>> list(zip(*matrix)) #星号是解包参数列表,上一讲已经介绍
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
所谓zip()
,就是用来组合列表的,例如:
>>> x = [1, 2, 3]
>>> y = [4, 5, 6]
>>> zipped = zip(x, y)
>>> list(zipped)
[(1, 4), (2, 5), (3, 6)]
zip()
是组合,星号*
是拆分,两者可以看作是互为逆操作:
>>> x2, y2 = zip(*zip(x, y)) # x2为元组(1,2,3),y2为元组(4,5,6)
>>> x == list(x2) and y == list(y2)
True
5.2. del 语句
前面介绍过删除列表中的元素有pop
和remove
,那能不能按照给定的索引而不是值来移除列表中的一个元素呢?可以,使用del
语句。
它不同于会返回一个值的 pop()
方法。 del
语句也可以用来从列表中移除切片或者清空整个列表(我们之前用过的方式是将一个空列表赋值给指定的切片)。 例如:
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]
del 甚至可以被用来删除整个变量,删除后再引用 a 时就会报错:
>>> del a