本文最后更新于:2024年5月7日 下午
虽然列表既灵活又简单,但面对各类需求时,我们可能会有更好的选择。本文记录 Python 中不同场景应该选择的列表结构。
列表不是首选时
- 比如要存放 1000 万个浮点数的话,数组(array)的效率要高 得多,因为数组在背后存的并不是 float 对象,而是数字的机器翻 译,也就是字节表述。这一点就跟 C 语言中的数组一样。
- 如果需要频繁对序列做先进先出的操作,deque(双端队列)的速度应该 会更快。
- 如果在代码里,包含操作(比如检查一个元素是否出现 在一个集合中)的频率很高,用 set(集合)会更合适。set 专为检查元素是否存在做过优化。但是它并不是序列,因为 set 是无序的。
数组
如果我们需要一个只包含数字的列表,那么 array.array
比 list 更 高效。数组支持所有跟可变序列有关的操作,包括 .pop
、.insert
和 .extend
。另外,数组还提供从文件读取和存入文件的更快的方法,如 .frombytes
和 .tofile
。
array 读写
- 一段读写 array 的代码
1 |
|
- 用 array.fromfile 从一个二进制文件里读出 1000 万个 双精度浮点数只需要 0.4 秒,这比从文本文件里读取的速度要快 60 倍,因为后者会使用内置的 float 方法把每一行文字转换成浮点数。
- 另外,使用 array.tofile 写入到二进制文件,比以每行一个浮点数的 方式把所有数字写入到文本文件要快 7 倍。另外,1000 万个这样的数 在二进制文件里只占用 80 000 000 个字节(每个浮点数占用 8 个字节, 不需要任何额外空间),如果是文本文件的话,我们需要 181 515 739 个字节。
内存视图
- memoryview 是一个内置类,它能让用户在不复制内容的情况下操作同 一个数组的不同切片。
- 内存视图其实是泛化和去数学化的 NumPy 数组。它让你在不需要 复制内容的前提下,在数据结构之间共享内存。其中数据结构可以 是任何形式,比如 PIL图片、SQLite 数据库和 NumPy 的数组,等 等。这个功能在处理大型数据集合的时候非常重要。
- memoryview.cast 的概念跟数组模块类似,能用不同的方式读写同一 块内存数据,而且内容字节不会随意移动。这听上去又跟 C 语言中类型 转换的概念差不多。memoryview.cast 会把同一块内存里的内容打包成一个全新的 memoryview 对象给你。
示例代码
通过改变数组中的一个字节来更新数组里某个元素的值
1 |
|
- 将内存中的数据看做单字节无符号数进行修改
- 在内存上的修改映射到了原始数据上
NumPy和SciPy
-
凭借着 NumPy 和 SciPy 提供的高阶数组和矩阵操作,Python 成为科学计 算应用的主流语言。NumPy 实现了多维同质数组(homogeneous array) 和矩阵,这些数据结构不但能处理数字,还能存放其他由用户定义的记 录。通过 NumPy,用户能对这些数据结构里的元素进行高效的操作。
-
SciPy 是基于 NumPy 的另一个库,它提供了很多跟科学计算有关的算 法,专为线性代数、数值积分和统计学而设计。SciPy 的高效和可靠性 归功于其背后的 C 和 Fortran 代码,而这些跟计算有关的部分都源自于 Netlib 库(http://www.netlib.org)。换句话说,SciPy 把基于 C 和 Fortran 的工业级数学计算功能用交互式且高度抽象的 Python 包装起来,让科学 家如鱼得水。
队列
双向队列
- 利用 .append 和 .pop 方法,我们可以把列表当作栈或者队列来用(比 如,把 .append 和 .pop(0) 合起来用,就能模拟栈的“先进先出”的特 点)。但是删除列表的第一个元素(抑或是在第一个元素之前添加一个 元素)之类的操作是很耗时的,因为这些操作会牵扯到移动列表里的所有元素。
- collections.deque 类(双向队列)是一个线程安全、可以快速从两 端添加或者删除元素的数据类型。而且如果想要有一种数据类型来存 放“最近用到的几个元素”,deque 也是一个很好的选择。这是因为在新 建一个双向队列的时候,你可以指定这个队列的大小,如果这个队列满 员了,还可以从反向端删除过期的元素,然后在尾端添加新的元素。
其他形式的队列
queue
- 提供了同步(线程安全)类 Queue、LifoQueue 和 PriorityQueue,不同的线程可以利用这些数据类型来交换信息。这三 个类的构造方法都有一个可选参数 maxsize,它接收正整数作为输入值,用来限定队列的大小。
- 但是在满员的时候,这些类不会扔掉旧的元 素来腾出位置。相反,如果队列满了,它就会被锁住,直到另外的线程 移除了某个元素而腾出了位置。这一特性让这些类很适合用来控制活跃线程的数量。
multiprocessing
- 这个包实现了自己的 Queue,它跟 queue.Queue 类似,是设计给 进程间通信用的。同时还有一个专门的 multiprocessing.JoinableQueue 类型,可以让任务管理变得更方 便。
asyncio
- Python 3.4 新提供的包,里面有 Queue、LifoQueue、PriorityQueue 和 JoinableQueue,这些类受 到 queue 和 multiprocessing 模块的影响,但是为异步编程里的任务管理提供了专门的便利。
heapq
- 跟上面三个模块不同的是,heapq 没有队列类,而是提供了 heappush 和 heappop 方法,让用户可以把可变序列当作堆队列或者优 先队列来使用。
参考资料
文章链接:
https://www.zywvvd.com/notes/coding/python/fluent-python/chapter-2/python-notlist/python-notlist/
“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”
微信支付
支付宝支付