虽然列表既灵活又简单,但面对各类需求时,我们可能会有更好的选择,比如:
1. 要存放 1000 万个浮点数的话,数组(array
)的效率要高得多,因为数组在背后存的并不是 float
对象,而是数字的机器翻译,也就是字节表述。
2. 如果需要频繁对序列做“先进先出”的操作,队列(deque
)的速度应该会更快。
2.9.1 数组
如果我们需要一个只包含数字的列表,那么
array.array
比list
更高效。数组支持所有跟可变序列有关的操作,包括.pop
、.insert
和.extend
。另外,数组还提供从文件读取和存入文件的更快的方法,如.frombytes
和.tofile
。
创建数组需要两个参数,第一个参数是类型码,用来表示在底层C语言中应该存放怎样的数据类型,比如 b 代表的是有符号字符,因此 array('b', ...) 创建的数组只能存放 1 个字节大小的整数,范围是 -128-127; Python 不允许你在数组中存放除指定数据类型外的数据;
示例 一个浮点型数组的创建、存入文件和从文件读取的过程
from array import array
from random import random
# 创建一个包含了100万个随机双精度浮点数的数组
floats = array('d', (random() for _ in range(10**7)))
fp = open('floats.bin', 'wb')
# tofile 将数组存放到二进制文件中
floats.tofile(fp)
# 新建一个双精度浮点空数组
floats_2 = array('d', [])
fp = open('floats.bin', 'rb')
# fromfile 从指定文件读取数组,第二个参数指定读取的数量
floats_2.fromfile(fp, 10**6) # 从二进制文件中读取出 100万个浮点数
print(floats == floats_2) # True
表2-2:列表和数组的属性和方法(不包含过期的数组方法以及那些由对象实现的方法)
| 列表 | 数组 |
|
---|---|---|---|
| • | • |
|
| • | • |
|
| • | • | 在尾部添加一个元素 |
|
| • | 翻转数组内每个元素的字节序列,转换字节序 |
| • |
| 删除所有元素 |
| • | • |
|
| • |
| 对列表浅复制 |
|
| • | 对 |
| • | • |
|
|
| • | 对 |
| • | • | 删除位置 |
| • | • | 将可迭代对象 |
|
| • | 将压缩成机器值的字节序列读出来添加到尾部 |
|
| • | 将二进制文件 |
|
| • | 将列表里的元素添加到尾部,如果其中任何一个元素导致了 |
| • | • |
|
| • | • | 找到 |
| • | • | 在位于 |
|
| • | 数组中每个元素的长度是几个字节 |
| • | • | 返回迭代器 |
| • | • |
|
| • | • |
|
| • | • |
|
| • | • |
|
| • | • | 删除位于 |
| • | • | 删除序列里第一次出现的 |
| • | • | 就地调转序列中元素的位置 |
| • |
| 返回一个从尾部开始扫描元素的迭代器 |
| • | • |
|
| • |
| 就地排序序列,可选参数有 |
|
| • | 把所有元素的机器值用 |
|
| • | 把所有元素以机器值的形式写入一个文件 |
|
| • | 把数组转换成列表,列表里的元素类型是数字对象 |
|
| • | 返回只有一个字符的字符串,代表数组元素在 C 语言中的类型 |
2.9.2 双向队列和其他形式的队列
利用 .append
和 .pop
方法,我们可以把列表当作栈或者队列来用(比如,把 .append
和 .pop(0)
合起来用,就能模拟队列的“先进先出”的特点)。但是删除列表的第一个元素(抑或是在第一个元素之前添加一个元素)之类的操作是很耗时的,因为这些操作会牵扯到移动列表里的所有元素。
collections.deque
类(双向队列)是一个线程安全、可以快速从两端添加或者删除元素的数据类型。而且如果想要有一种数据类型来存放“最近用到的几个元素”,deque
也是一个很好的选择。这是因为在新建一个双向队列的时候,你可以指定这个队列的大小,如果这个队列满员了,还可以从反向端删除过期的元素,然后在尾端添加新的元素。
示例 2-23 使用双向队列
from collections import deque
# 创建个新的队列,第一个参数是队列的内容,应该是个可迭代对象,maxlen 参数是指定队列大小
dq = deque(range(10), maxlen=10)
print(dq) # deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
# 当参数 n>0 时,将最右侧 n 个元素移动到队列左边,默认 n=1;
dq.rotate(3)
print(dq) # deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
# 当 n<0 时,将最左侧 n 个元素移动到右边,默认 n=1;
dq.rotate(-3)
print(dq) # deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
# 当队列满了之后,在尾部添加元素的时候,会将头部的元素删掉
dq.append(10)
print(dq) # deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], maxlen=10)
# 当队列满了之后,在头添加元素的时候,会将尾部的元素删掉
dq.appendleft(0)
print(dq) # deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
# extend 方法同 append,不过添加的是可迭代对象
dq.extend([...])
dq.extendleft([...])
表2-3:列表和双向队列的方法(不包括由对象实现的方法)
| 列表 | 双向队列 |
|
---|---|---|---|
| • |
|
|
| • | • |
|
| • | • | 添加一个元素到最右侧(到最后一个元素之后) |
|
| • | 添加一个元素到最左侧(到第一个元素之前) |
| • | • | 删除所有元素 |
| • |
|
|
| • |
| 对列表浅复制 |
|
| • | 对 |
| • | • |
|
| • | • | 把位置 |
| • | • | 将可迭代对象 |
|
| • | 将可迭代对象 |
| • | • |
|
| • |
| 找到 |
| • |
| 在位于 |
| • | • | 返回迭代器 |
| • | • |
|
| • |
|
|
| • |
|
|
| • |
|
|
| • | • | 移除最后一个元素并返回它的值# |
|
| • | 移除第一个元素并返回它的值 |
| • | • | 移除序列里第一次出现的 |
| • | • | 调转序列中元素的位置 |
| • | • | 返回一个从尾部开始扫描元素的迭代器 |
|
| • | 把 |
| • | • |
|
| • |
| 就地排序序列,可选参数有 |