《流畅的python》 2.9 当列表不是首选时 读书笔记
虽然列表既灵活又简单,但面对各类需求时,我们可能会有更好的选择。比如,要存放 1000 万个浮点数的话,数组(array)的效率要高得多,因为数组在背后存的并不是 float 对象,而是数字的机器翻译,也就是字节表述。
如果需要频繁对序列做先进先出的操作,deque(双端队列)的速度应该会更快。
如果在你的代码里,包含操作(比如检查一个元素是否出现在一个集合中)的 频率很高,用 set(集合)会更合适。set 专为检查元素是否存在做过优化。但是它 并不是序列,因为 set 是无序的。
一、数组
如果我们需要一个只包含数字的列表,那么 array.array 比 list 更高效。数组支持所 有跟可变序列有关的操作,包括 .pop、.insert 和 .extend。另外,数组还提供从文件 读取和存入文件的更快的方法,如 .frombytes 和 .tofile。
例子:从创建一个有 1000 万个随机浮点数的数组开始,到如何把这个数组存放 到文件里,再到如何从文件读取这个数组。
from array import array # 引入 array 类型。
from random import random
floats = array('d', (random() for i in range(10 ** 7))) # 利用一个可迭代对象来建立一个双精度浮点数组(类型码是 'd'),这里我们用的可迭代对象是一个生成器表达式。
print(floats[-1]) # 查看数组的最后一个元素。
fp = open('floats.bin', 'wb')
floats.tofile(fp) # 把数组存入一个二进制文件里。
fp.close()
floats2 = array('d') # 新建一个双精度浮点空数组。
fp = open('floats.bin', 'rb')
floats2.fromfile(fp, 10**7) # 把1000万个浮点数从二进制文件里读取出来。
fp.close()
print(floats2[-1]) # 查看新数组的最后一个元素。
if floats2 == floats: # 检查两个数组的内容是不是完全一样。
print(True)
else:
print(False)
从上面的代码我们能得出结论,array.tofile 和 array.fromfile 用起来很简单。
把 这段代码跑一跑,你还会发现它的速度也很快。一个小试验告诉我,用 array.fromfile 从一个二进制文件里读出 1000 万个双精度浮点数只需要 0.1 秒,这比从文本文件里读取 的速度要快 60 倍,因为后者会使用内置的 float 方法把每一行文字转换成浮点数。另 外,使用 array.tofile 写入到二进制文件,比以每行一个浮点数的方式把所有数字写 入到文本文件要快 7 倍。另外,1000 万个这样的数在二进制文件里只占用 80 000 000 个 字节(每个浮点数占用 8 个字节,不需要任何额外空间,如果是文本文件的话,我们需 要 181 515 739 个字节。
另外一个快速序列化数字类型的方法是使用 pickle模块。pickle.dump 处理浮点 数组的速度几乎跟 array.tofile 一样快。不过前者可以处理几乎所有的内置数字 类型,包含复数、嵌套集合,甚至用户自定义的类。
列表 | 数组 | ||
---|---|---|---|
s.add(s2) | • | • | • |